diff --git a/Corona-Warn-App/build.gradle b/Corona-Warn-App/build.gradle index f8ffb33b2ae96d524de4addad7bae3bf4e6d66ec..bd3980e016d94a4a247d65c42f9ab7355bf17d0d 100644 --- a/Corona-Warn-App/build.gradle +++ b/Corona-Warn-App/build.gradle @@ -322,6 +322,11 @@ dependencies { implementation 'androidx.preference:preference-ktx:1.1.1' implementation 'androidx.work:work-runtime-ktx:2.5.0' + def activity_version = "1.2.3" + implementation "androidx.activity:activity-ktx:$activity_version" + def fragment_version = "1.3.5" + implementation "androidx.fragment:fragment-ktx:$fragment_version" + implementation 'androidx.lifecycle:lifecycle-common-java8:2.3.1' implementation 'androidx.lifecycle:lifecycle-process:2.3.1' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' @@ -362,7 +367,8 @@ dependencies { testImplementation 'org.hamcrest:hamcrest-library:2.2' // Testing - jUnit4 - testImplementation 'junit:junit:4.13.1' + def junit_version = "4.13.1" + testImplementation "junit:junit:$junit_version" testImplementation "org.junit.vintage:junit-vintage-engine:5.7.0" testImplementation "androidx.test:core-ktx:1.3.0" @@ -383,7 +389,7 @@ dependencies { testImplementation "io.github.classgraph:classgraph:4.8.90" // Testing - Instrumentation - androidTestImplementation 'junit:junit:4.13.1' + androidTestImplementation "junit:junit:$junit_version" androidTestImplementation 'androidx.test:runner:1.3.0' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/test/ui/CovidCertificateDetailsFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/test/ui/CovidCertificateDetailsFragmentTest.kt index 124ba8e741f7dd6c8c8adaa600eb8bc0b3cf5617..aab4a8c7249b5998f16f85e1ede7a8f87dfde908 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/test/ui/CovidCertificateDetailsFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/test/ui/CovidCertificateDetailsFragmentTest.kt @@ -17,9 +17,9 @@ import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePerso import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateIdentifier -import de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsFragment -import de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsFragmentArgs import de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsViewModel +import de.rki.coronawarnapp.covidcertificate.test.ui.details.TestCertificateDetailsFragment +import de.rki.coronawarnapp.covidcertificate.test.ui.details.TestCertificateDetailsFragmentArgs import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK @@ -43,7 +43,7 @@ class VaccinationDetailsFragmentTest : BaseUITest() { @MockK lateinit var vaccinationDetailsViewModel: CovidCertificateDetailsViewModel @MockK lateinit var certificatePersonIdentifier: CertificatePersonIdentifier - private val args = CovidCertificateDetailsFragmentArgs("testCertificateIdentifier").toBundle() + private val args = TestCertificateDetailsFragmentArgs("testCertificateIdentifier").toBundle() @Before fun setUp() { @@ -61,17 +61,17 @@ class VaccinationDetailsFragmentTest : BaseUITest() { @Test fun launch_fragment() { - launchFragment2<CovidCertificateDetailsFragment>(fragmentArgs = args) + launchFragment2<TestCertificateDetailsFragment>(fragmentArgs = args) } @Screenshot @Test fun capture_screenshot_incomplete() { every { vaccinationDetailsViewModel.covidCertificate } returns vaccinationDetailsData() - launchFragmentInContainer2<CovidCertificateDetailsFragment>(fragmentArgs = args) - takeScreenshot<CovidCertificateDetailsFragment>() + launchFragmentInContainer2<TestCertificateDetailsFragment>(fragmentArgs = args) + takeScreenshot<TestCertificateDetailsFragment>() onView(withId(R.id.coordinator_layout)).perform(swipeUp()) - takeScreenshot<CovidCertificateDetailsFragment>("_2") + takeScreenshot<TestCertificateDetailsFragment>("_2") } private fun bitmapLiveDate(): LiveData<Bitmap> { @@ -143,5 +143,5 @@ class VaccinationDetailsFragmentTest : BaseUITest() { @Module abstract class CovidCertificateDetailsFragmentTestModule { @ContributesAndroidInjector - abstract fun covidCertificateDetailsFragment(): CovidCertificateDetailsFragment + abstract fun covidCertificateDetailsFragment(): TestCertificateDetailsFragment } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/DigitalCovidCertificateUIModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/DigitalCovidCertificateUIModule.kt index 8e02465742e84562950ac2af89d0d9fda6845466..fdeb2f4205806ff55f0da7b4fd4fe4808bbc1f89 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/DigitalCovidCertificateUIModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/DigitalCovidCertificateUIModule.kt @@ -1,6 +1,9 @@ package de.rki.coronawarnapp.covidcertificate import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.covidcertificate.common.scan.DccQrCodeScanFragment +import de.rki.coronawarnapp.covidcertificate.common.scan.DccQrCodeScanModule import de.rki.coronawarnapp.covidcertificate.recovery.ui.RecoveryCertificateUIModule import de.rki.coronawarnapp.covidcertificate.test.ui.TestCertificateUIModule import de.rki.coronawarnapp.covidcertificate.vaccination.ui.VaccinationCertificateUIModule @@ -12,4 +15,8 @@ import de.rki.coronawarnapp.covidcertificate.vaccination.ui.VaccinationCertifica RecoveryCertificateUIModule::class, ] ) -abstract class DigitalCovidCertificateUIModule +abstract class DigitalCovidCertificateUIModule { + + @ContributesAndroidInjector(modules = [DccQrCodeScanModule::class]) + abstract fun dccQrCodeScanFragment(): DccQrCodeScanFragment +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanFragment.kt similarity index 72% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanFragment.kt index 5fc1ecbdd0e643141a2baca8be7e111dd8aed511..66a4c1c28f9dc541537c14749c78829a86d63bf8 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanFragment.kt @@ -1,10 +1,10 @@ -package de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan +package de.rki.coronawarnapp.covidcertificate.common.scan import android.Manifest -import android.content.pm.PackageManager import android.os.Bundle import android.view.View import android.view.accessibility.AccessibilityEvent.TYPE_ANNOUNCEMENT +import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment import com.google.zxing.BarcodeFormat import com.journeyapps.barcodescanner.DefaultDecoderFactory @@ -23,16 +23,32 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModels import javax.inject.Inject -class VaccinationQrCodeScanFragment : +class DccQrCodeScanFragment : Fragment(R.layout.fragment_scan_qr_code), AutoInject { @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory - private val viewModel: VaccinationQrCodeScanViewModel by cwaViewModels { viewModelFactory } + private val viewModel: DccQrCodeScanViewModel by cwaViewModels { viewModelFactory } private val binding: FragmentScanQrCodeBinding by viewBinding() private var showsPermissionDialog = false + val requestPermissionLauncher = + registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted: Boolean -> + if (!isGranted) { + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { + showCameraPermissionRationaleDialog() + viewModel.setCameraDeniedPermanently(false) + } else { + // User permanently denied access to the camera + showCameraPermissionDeniedDialog() + viewModel.setCameraDeniedPermanently(true) + } + } + } + override fun onViewCreated( view: View, savedInstanceState: Bundle? @@ -50,16 +66,27 @@ class VaccinationQrCodeScanFragment : viewModel.event.observe(viewLifecycleOwner) { event -> when (event) { - is VaccinationQrCodeScanViewModel.Event.QrCodeScanSucceeded -> { + is DccQrCodeScanViewModel.Event.VaccinationQrCodeScanSucceeded -> { binding.qrCodeScanSpinner.hide() doNavigate( - VaccinationQrCodeScanFragmentDirections - .actionVaccinationQrCodeScanFragmentToVaccinationListFragment( - event.personIdentifierCodeSha256 + DccQrCodeScanFragmentDirections + .actionDccQrCodeScanFragmentToVaccinationDetailsFragment( + event.certificateId ) ) } - VaccinationQrCodeScanViewModel.Event.QrCodeScanInProgress -> { + is DccQrCodeScanViewModel.Event.RecoveryQrCodeScanSucceeded -> { // TODO + } + is DccQrCodeScanViewModel.Event.TestQrCodeScanSucceeded -> { + binding.qrCodeScanSpinner.hide() + doNavigate( + DccQrCodeScanFragmentDirections + .actionDccQrCodeScanFragmentToTestCertificateDetailsFragment( + event.certificateId + ) + ) + } + DccQrCodeScanViewModel.Event.QrCodeScanInProgress -> { binding.qrCodeScanSpinner.show() } } @@ -91,25 +118,6 @@ class VaccinationQrCodeScanFragment : requestCameraPermission() } - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array<String>, - grantResults: IntArray - ) { - if (requestCode == REQUEST_CAMERA_PERMISSION_CODE && - grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_DENIED - ) { - if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { - showCameraPermissionRationaleDialog() - viewModel.setCameraDeniedPermanently(false) - } else { - // User permanently denied access to the camera - showCameraPermissionDeniedDialog() - viewModel.setCameraDeniedPermanently(true) - } - } - } - private fun startDecode() = binding.qrCodeScanPreview .decodeSingle { barcodeResult -> viewModel.onScanResult(barcodeResult) @@ -151,10 +159,7 @@ class VaccinationQrCodeScanFragment : DialogHelper.showDialog(cameraPermissionRationaleDialogInstance) } - private fun requestCameraPermission() = requestPermissions( - arrayOf(Manifest.permission.CAMERA), - REQUEST_CAMERA_PERMISSION_CODE - ) + private fun requestCameraPermission() = requestPermissionLauncher.launch(Manifest.permission.CAMERA) private fun leave() { showsPermissionDialog = false @@ -165,8 +170,4 @@ class VaccinationQrCodeScanFragment : super.onPause() binding.qrCodeScanPreview.pause() } - - companion object { - private const val REQUEST_CAMERA_PERMISSION_CODE = 4000 - } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanModule.kt similarity index 54% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanModule.kt index bc581306a761d7bdfd9e46489b4ca2f98e401dc8..0954b8f29fd35fe26ab23c3c6fd4abba9fc6dd8a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan +package de.rki.coronawarnapp.covidcertificate.common.scan import dagger.Binds import dagger.Module @@ -8,11 +8,11 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey @Module -abstract class VaccinationQrCodeScanModule { +abstract class DccQrCodeScanModule { @Binds @IntoMap - @CWAViewModelKey(VaccinationQrCodeScanViewModel::class) - abstract fun vaccinationQrCodeScanFragment( - factory: VaccinationQrCodeScanViewModel.Factory + @CWAViewModelKey(DccQrCodeScanViewModel::class) + abstract fun dccQrCodeScanFragment( + factory: DccQrCodeScanViewModel.Factory ): CWAViewModelFactory<out CWAViewModel> } 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 new file mode 100644 index 0000000000000000000000000000000000000000..75835156028e515fa19d8021ecdffa1796d82ff5 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanViewModel.kt @@ -0,0 +1,77 @@ +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.recovery.core.RecoveryCertificateRepository +import de.rki.coronawarnapp.covidcertificate.recovery.core.qrcode.RecoveryCertificateQRCode +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository +import de.rki.coronawarnapp.covidcertificate.test.core.qrcode.TestCertificateQRCode +import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.DccQrCodeValidator +import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.VaccinationCertificateQRCode +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository +import de.rki.coronawarnapp.util.permission.CameraSettings +import de.rki.coronawarnapp.util.ui.SingleLiveEvent +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory +import timber.log.Timber + +class DccQrCodeScanViewModel @AssistedInject constructor( + private val cameraSettings: CameraSettings, + private val qrCodeValidator: DccQrCodeValidator, + private val vaccinationRepository: VaccinationRepository, + private val testCertificateRepository: TestCertificateRepository, + private val recoveryCertificateRepository: RecoveryCertificateRepository +) : CWAViewModel() { + + val event = SingleLiveEvent<Event>() + + val errorEvent = SingleLiveEvent<Throwable>() + + fun onScanResult(barcodeResult: BarcodeResult) = launch { + try { + event.postValue(Event.QrCodeScanInProgress) + when (val qrCode = qrCodeValidator.validate(barcodeResult.text)) { + is VaccinationCertificateQRCode -> registerVaccinationCertificate(qrCode) + is TestCertificateQRCode -> registerTestCertificate(qrCode) + is RecoveryCertificateQRCode -> registerRecoveryCertificate(qrCode) + } + } catch (e: Throwable) { + errorEvent.postValue(e) + } + } + + private suspend fun registerVaccinationCertificate(qrCode: VaccinationCertificateQRCode) { + val certificate = vaccinationRepository.registerVaccination(qrCode) + event.postValue(Event.VaccinationQrCodeScanSucceeded(certificate.certificateId)) + } + + private suspend fun registerTestCertificate(qrCode: TestCertificateQRCode) { + throw NotImplementedError("Test certificate found") + // TODO +// val certificate = testCertificateRepository.requestCertificate(qrCode) +// event.postValue(Event.TestQrCodeScanSucceeded(certificate.certificateId)) + } + + private suspend fun registerRecoveryCertificate(qrCode: RecoveryCertificateQRCode) { + throw NotImplementedError("Recovery certificate found") + // TODO +// val certificate = recoveryCertificateRepository.requestCertificate(qrCode) +// event.postValue(Event.RecoveryQrCodeScanSucceeded(certificate.certificateId)) + } + + fun setCameraDeniedPermanently(denied: Boolean) { + Timber.d("setCameraDeniedPermanently(denied=$denied)") + cameraSettings.isCameraDeniedPermanently.update { denied } + } + + sealed class Event { + object QrCodeScanInProgress : Event() + data class VaccinationQrCodeScanSucceeded(val certificateId: String) : Event() + data class TestQrCodeScanSucceeded(val certificateId: String) : Event() + data class RecoveryQrCodeScanSucceeded(val certificateId: String) : Event() + } + + @AssistedFactory + interface Factory : SimpleCWAViewModelFactory<DccQrCodeScanViewModel> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewFragment.kt index 106e4a95c6da1df1950dc3e07c610244aaf60c6d..475f3e4701ae2805f7a35d99f7580b915caeac48 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewFragment.kt @@ -2,7 +2,6 @@ package de.rki.coronawarnapp.covidcertificate.person.ui.overview import android.os.Bundle import android.view.View -import android.widget.Toast import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.fragment.app.Fragment @@ -14,8 +13,8 @@ import de.rki.coronawarnapp.covidcertificate.common.exception.TestCertificateSer import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.CameraPermissionCard import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.CertificatesItem import de.rki.coronawarnapp.databinding.PersonOverviewFragmentBinding -import de.rki.coronawarnapp.util.ExternalActionHelper.openUrl import de.rki.coronawarnapp.util.ExternalActionHelper.openAppDetailsSettings +import de.rki.coronawarnapp.util.ExternalActionHelper.openUrl import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.lists.decorations.TopBottomPaddingDecorator import de.rki.coronawarnapp.util.lists.diffutil.update @@ -81,11 +80,9 @@ class PersonOverviewFragment : Fragment(R.layout.person_overview_fragment), Auto }.show() } - ScanQrCode -> Toast.makeText( - requireContext(), - "TODO \uD83D\uDEA7 Tomorrow maybe?!", - Toast.LENGTH_LONG - ).show() + ScanQrCode -> doNavigate( + PersonOverviewFragmentDirections.actionPersonOverviewFragmentToDccQrCodeScanFragment() + ) OpenAppDeviceSettings -> openAppDetailsSettings() } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/TestCertificateUIModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/TestCertificateUIModule.kt index 23e9fa4035cc3f7fa118c2e5421ce768e3251e5f..d1d1a28cf47b61e0e321df9c397fae0913b5d9f6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/TestCertificateUIModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/TestCertificateUIModule.kt @@ -4,8 +4,8 @@ import dagger.Module import dagger.android.ContributesAndroidInjector import de.rki.coronawarnapp.covidcertificate.person.ui.overview.PersonOverviewFragment import de.rki.coronawarnapp.covidcertificate.person.ui.overview.PersonOverviewFragmentModule -import de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsFragment import de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsModule +import de.rki.coronawarnapp.covidcertificate.test.ui.details.TestCertificateDetailsFragment @Module abstract class TestCertificateUIModule { @@ -14,7 +14,7 @@ abstract class TestCertificateUIModule { abstract fun certificatesFragment(): CertificatesFragment @ContributesAndroidInjector(modules = [CovidCertificateDetailsModule::class]) - abstract fun certificateDetailsFragment(): CovidCertificateDetailsFragment + abstract fun certificateDetailsFragment(): TestCertificateDetailsFragment @ContributesAndroidInjector(modules = [PersonOverviewFragmentModule::class]) abstract fun personOverviewFragment(): PersonOverviewFragment diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/TestCertificateDetailsFragment.kt similarity index 87% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/TestCertificateDetailsFragment.kt index 670da564f0f1f45af487c4cdca23c5ab9a2fa4f9..8d41b1c7c2c4f1701ed9b329e5b7919c9275e6dd 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/TestCertificateDetailsFragment.kt @@ -13,7 +13,7 @@ import com.google.android.material.appbar.AppBarLayout import de.rki.coronawarnapp.R import de.rki.coronawarnapp.bugreporting.ui.toErrorDialogBuilder import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate -import de.rki.coronawarnapp.databinding.FragmentCovidCertificateDetailsBinding +import de.rki.coronawarnapp.databinding.FragmentTestCertificateDetailsBinding import de.rki.coronawarnapp.ui.qrcode.fullscreen.QrCodeFullScreenFragmentArgs import de.rki.coronawarnapp.ui.view.onOffsetChange import de.rki.coronawarnapp.util.DialogHelper @@ -27,11 +27,11 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted import javax.inject.Inject -class CovidCertificateDetailsFragment : Fragment(R.layout.fragment_covid_certificate_details), AutoInject { +class TestCertificateDetailsFragment : Fragment(R.layout.fragment_test_certificate_details), AutoInject { @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory - private val binding by viewBinding<FragmentCovidCertificateDetailsBinding>() - private val args by navArgs<CovidCertificateDetailsFragmentArgs>() + private val binding by viewBinding<FragmentTestCertificateDetailsBinding>() + private val args by navArgs<TestCertificateDetailsFragmentArgs>() private val viewModel: CovidCertificateDetailsViewModel by cwaViewModelsAssisted( factoryProducer = { viewModelFactory }, constructorCall = { factory, _ -> @@ -57,7 +57,7 @@ class CovidCertificateDetailsFragment : Fragment(R.layout.fragment_covid_certifi viewModel.covidCertificate.observe(viewLifecycleOwner) { it?.let { onCertificateReady(it) } } } - private fun FragmentCovidCertificateDetailsBinding.onCertificateReady( + private fun FragmentTestCertificateDetailsBinding.onCertificateReady( testCertificate: TestCertificate ) { name.text = testCertificate.run { "$lastName, $firstName" } @@ -77,7 +77,7 @@ class CovidCertificateDetailsFragment : Fragment(R.layout.fragment_covid_certifi certificateId.text = testCertificate.certificateId } - private fun FragmentCovidCertificateDetailsBinding.onQrCodeReady(bitmap: Bitmap?) { + private fun FragmentTestCertificateDetailsBinding.onQrCodeReady(bitmap: Bitmap?) { qrCodeCard.apply { image.setImageBitmap(bitmap) progressBar.hide() @@ -85,12 +85,12 @@ class CovidCertificateDetailsFragment : Fragment(R.layout.fragment_covid_certifi } } - private fun FragmentCovidCertificateDetailsBinding.onError(error: Throwable) { + private fun FragmentTestCertificateDetailsBinding.onError(error: Throwable) { qrCodeCard.progressBar.hide() error.toErrorDialogBuilder(requireContext()).show() } - private fun FragmentCovidCertificateDetailsBinding.onNavEvent(event: CovidCertificateDetailsNavigation) { + private fun FragmentTestCertificateDetailsBinding.onNavEvent(event: CovidCertificateDetailsNavigation) { when (event) { CovidCertificateDetailsNavigation.Back -> popBackStack() is CovidCertificateDetailsNavigation.FullQrCode -> findNavController().navigate( @@ -102,7 +102,7 @@ class CovidCertificateDetailsFragment : Fragment(R.layout.fragment_covid_certifi } } - private fun FragmentCovidCertificateDetailsBinding.bindToolbar() = toolbar.apply { + private fun FragmentTestCertificateDetailsBinding.bindToolbar() = toolbar.apply { setNavigationOnClickListener { popBackStack() } setOnMenuItemClickListener { when (it.itemId) { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/VaccinationCertificateUIModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/VaccinationCertificateUIModule.kt index 961a18479dd77f5d4a91411fd79a69c9f8f79851..f2778682c41b272c1970234dd93bfe259638ed93 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/VaccinationCertificateUIModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/VaccinationCertificateUIModule.kt @@ -8,8 +8,6 @@ import de.rki.coronawarnapp.covidcertificate.vaccination.ui.details.VaccinationD import de.rki.coronawarnapp.covidcertificate.vaccination.ui.details.VaccinationDetailsFragmentModule import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListFragment import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListFragmentModule -import de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan.VaccinationQrCodeScanFragment -import de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan.VaccinationQrCodeScanModule @Module abstract class VaccinationCertificateUIModule { @@ -20,9 +18,6 @@ abstract class VaccinationCertificateUIModule { @ContributesAndroidInjector(modules = [VaccinationDetailsFragmentModule::class]) abstract fun vaccinationDetailsFragment(): VaccinationDetailsFragment - @ContributesAndroidInjector(modules = [VaccinationQrCodeScanModule::class]) - abstract fun vaccinationQrCodeScanFragment(): VaccinationQrCodeScanFragment - @ContributesAndroidInjector(modules = [VaccinationConsentFragmentModule::class]) abstract fun vaccinationConsentFragment(): VaccinationConsentFragment } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragment.kt index 0960f36b80d642062e182601974c309571e11358..9fbf7a9d937c768f78d67dd5676ceef83c213df4 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragment.kt @@ -17,8 +17,8 @@ import de.rki.coronawarnapp.bugreporting.ui.toErrorDialogBuilder import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListViewModel.Event.DeleteVaccinationEvent import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListViewModel.Event.NavigateBack +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListViewModel.Event.NavigateToQrCodeScanScreen import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListViewModel.Event.NavigateToVaccinationCertificateDetails -import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListViewModel.Event.NavigateToVaccinationQrCodeScanScreen import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.VaccinationListAdapter import de.rki.coronawarnapp.databinding.FragmentVaccinationListBinding import de.rki.coronawarnapp.ui.qrcode.fullscreen.QrCodeFullScreenFragmentArgs @@ -74,8 +74,8 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au VaccinationListFragmentDirections .actionVaccinationListFragmentToVaccinationDetailsFragment(event.vaccinationCertificateId) ) - is NavigateToVaccinationQrCodeScanScreen -> doNavigate( - VaccinationListFragmentDirections.actionVaccinationListFragmentToVaccinationQrCodeScanFragment() + is NavigateToQrCodeScanScreen -> doNavigate( + VaccinationListFragmentDirections.actionVaccinationListFragmentToDccQrCodeScanFragment() ) is VaccinationListViewModel.Event.NavigateToQrCodeFullScreen -> { val navigatorExtras = diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListViewModel.kt index 9dea2c3500fe560abdba6ef75945cd14967b6087..60fec58922449917e8efcc01eaa36ef4778a38eb 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListViewModel.kt @@ -142,7 +142,7 @@ class VaccinationListViewModel @AssistedInject constructor( }.toList() fun onRegisterNewVaccinationClick() { - events.postValue(Event.NavigateToVaccinationQrCodeScanScreen) + events.postValue(Event.NavigateToQrCodeScanScreen) } fun deleteVaccination(vaccinationCertificateId: String) { @@ -163,7 +163,7 @@ class VaccinationListViewModel @AssistedInject constructor( sealed class Event { data class NavigateToVaccinationCertificateDetails(val vaccinationCertificateId: String) : Event() - object NavigateToVaccinationQrCodeScanScreen : Event() + object NavigateToQrCodeScanScreen : Event() data class NavigateToQrCodeFullScreen(val qrCode: String, val positionInList: Int) : Event() data class DeleteVaccinationEvent(val vaccinationCertificateId: String, val position: Int? = null) : Event() object NavigateBack : Event() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt deleted file mode 100644 index 64dfd89b0c4abfb147ee57d8bb2ae511f0f0a58e..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt +++ /dev/null @@ -1,53 +0,0 @@ -package de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan - -import com.journeyapps.barcodescanner.BarcodeResult -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject -import de.rki.coronawarnapp.covidcertificate.common.exception.InvalidHealthCertificateException.ErrorCode.NO_VACCINATION_ENTRY -import de.rki.coronawarnapp.covidcertificate.common.exception.InvalidVaccinationCertificateException -import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.DccQrCodeValidator -import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.VaccinationCertificateQRCode -import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository -import de.rki.coronawarnapp.util.permission.CameraSettings -import de.rki.coronawarnapp.util.ui.SingleLiveEvent -import de.rki.coronawarnapp.util.viewmodel.CWAViewModel -import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory -import timber.log.Timber - -class VaccinationQrCodeScanViewModel @AssistedInject constructor( - private val cameraSettings: CameraSettings, - private val vaccinationQRCodeValidator: DccQrCodeValidator, - private val vaccinationRepository: VaccinationRepository -) : CWAViewModel() { - - val event = SingleLiveEvent<Event>() - - val errorEvent = SingleLiveEvent<Throwable>() - - fun onScanResult(barcodeResult: BarcodeResult) = launch { - try { - event.postValue(Event.QrCodeScanInProgress) - val qrCode = vaccinationQRCodeValidator.validate(barcodeResult.text) - if (qrCode !is VaccinationCertificateQRCode) { - throw InvalidVaccinationCertificateException(NO_VACCINATION_ENTRY) - } - val vaccinationCertificate = vaccinationRepository.registerVaccination(qrCode) - event.postValue(Event.QrCodeScanSucceeded(vaccinationCertificate.personIdentifier.codeSHA256)) - } catch (e: Throwable) { - errorEvent.postValue(e) - } - } - - fun setCameraDeniedPermanently(denied: Boolean) { - Timber.d("setCameraDeniedPermanently(denied=$denied)") - cameraSettings.isCameraDeniedPermanently.update { denied } - } - - sealed class Event { - object QrCodeScanInProgress : Event() - data class QrCodeScanSucceeded(val personIdentifierCodeSha256: String) : Event() - } - - @AssistedFactory - interface Factory : SimpleCWAViewModelFactory<VaccinationQrCodeScanViewModel> -} diff --git a/Corona-Warn-App/src/main/res/layout/fragment_covid_certificate_details.xml b/Corona-Warn-App/src/main/res/layout/fragment_test_certificate_details.xml similarity index 100% rename from Corona-Warn-App/src/main/res/layout/fragment_covid_certificate_details.xml rename to Corona-Warn-App/src/main/res/layout/fragment_test_certificate_details.xml diff --git a/Corona-Warn-App/src/main/res/navigation/certificate_graph.xml b/Corona-Warn-App/src/main/res/navigation/certificate_graph.xml index 8e21587d789c80a60f3b3f84e4d139ad168dca59..c9e3e74b14b1e9b519c224430baaaf541f106374 100644 --- a/Corona-Warn-App/src/main/res/navigation/certificate_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/certificate_graph.xml @@ -15,6 +15,9 @@ <action android:id="@+id/action_personOverviewFragment_to_vaccinationListFragment" app:destination="@id/vaccinationListFragment" /> + <action + android:id="@+id/action_personOverviewFragment_to_dccQrCodeScanFragment" + app:destination="@id/dccQrCodeScanFragment" /> </fragment> <fragment @@ -43,10 +46,10 @@ tools:layout="@layout/fragment_information_privacy" /> <fragment - android:id="@+id/covidCertificateDetailsFragment" - android:name="de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsFragment" + android:id="@+id/testCertificateDetailsFragment" + android:name="de.rki.coronawarnapp.covidcertificate.test.ui.details.TestCertificateDetailsFragment" android:label="CovidCertificateDetailsFragment" - tools:layout="@layout/fragment_covid_certificate_details"> + tools:layout="@layout/fragment_test_certificate_details"> <argument android:name="testCertificateIdentifier" @@ -54,16 +57,20 @@ </fragment> <fragment - android:id="@+id/vaccinationQrCodeScanFragment" - android:name="de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan.VaccinationQrCodeScanFragment" - android:label="VaccinationQrCodeScanFragment" + android:id="@+id/dccQrCodeScanFragment" + android:name="de.rki.coronawarnapp.covidcertificate.common.scan.DccQrCodeScanFragment" + android:label="DccQrCodeScanFragment" tools:layout="@layout/fragment_scan_qr_code"> <action - android:id="@+id/action_vaccinationQrCodeScanFragment_to_vaccinationListFragment" - app:destination="@id/vaccinationListFragment" - app:launchSingleTop="true" - app:popUpTo="@id/vaccinationQrCodeScanFragment" - app:popUpToInclusive="true"/> + 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" /> </fragment> <fragment @@ -79,11 +86,11 @@ app:destination="@id/vaccinationDetailsFragment" /> <deepLink app:uri="coronawarnapp://vaccination-list/{personIdentifierCodeSha256}" /> <action - android:id="@+id/action_vaccinationListFragment_to_vaccinationQrCodeScanFragment" - app:destination="@id/vaccinationQrCodeScanFragment" /> + android:id="@+id/action_vaccinationListFragment_to_dccQrCodeScanFragment" + app:destination="@id/dccQrCodeScanFragment" /> <action android:id="@+id/action_vaccinationListFragment_to_covidCertificateDetailsFragment" - app:destination="@id/covidCertificateDetailsFragment" /> + app:destination="@id/testCertificateDetailsFragment" /> </fragment> <fragment @@ -95,4 +102,4 @@ android:name="vaccinationCertificateId" app:argType="string" /> </fragment> -</navigation> \ No newline at end of file +</navigation>