From b8617990856738ce440b2589f40e99e563e62585 Mon Sep 17 00:00:00 2001 From: Mohamed <mohamed.metwalli@sap.com> Date: Thu, 11 Mar 2021 09:45:50 +0100 Subject: [PATCH] Create Check-in confirmation screen (EXPOSUREAPP-5424) (#2536) * Refactoring - Add basic setup for attendee and organizer - Renaming and re-packing for already implemented screens * Rename * Add check in tab * lint * Connect scan fragment * Navigate to confirm event * Fix import * lint * Add FAB text * Update MDC version * Catch error * Animate transition * Add space * Connect check-in flow * Parse signed event * Import SingleLiveData * Add test * Clean-up * Delete ConfirmCheckInViewModel.kt * Support new deeplink host and requirements * Validate uri * Update LauncherActivityTest.kt * Renaming * Trace location times are in seconds * Delete redundants * Remove destinations from main graph those destinations are part of attendee graph now * Use hard coded string * Verify uri * Pass QRCodeVerifyResult directly * lint --- Corona-Warn-App/build.gradle | 2 +- .../checkin/VerifiedTraceLocationKtTest.kt | 40 ++++++++++++ .../ui/launcher/LauncherActivityTest.kt | 8 +-- .../java/de/rki/coronawarnapp/util/UriTest.kt | 45 ------------- .../ui/qrcode/QrCodeCreationTestFragment.kt | 7 ++ .../layout/fragment_test_qrcode_creation.xml | 1 - .../res/navigation/test_nav_graph.xml | 2 +- .../deviceForTesters/res/values/strings.xml | 4 -- Corona-Warn-App/src/main/AndroidManifest.xml | 7 +- .../EventRegistrationModule.kt | 1 - .../checkins/qrcode/UriValidator.kt | 24 +++++++ .../de/rki/coronawarnapp/ui/UIExtensions.kt | 1 + .../EventRegistrationUIModule.kt | 13 ++-- .../attendee/checkin/CheckInsFragment.kt | 65 +++++++++++++++++++ .../attendee/checkin/CheckInsModule.kt | 18 +++++ .../attendee/checkin/CheckInsViewModel.kt | 43 ++++++++++++ .../attendee/checkin/VerifiedTraceLocation.kt | 35 ++++++++++ .../confirm}/ConfirmCheckInFragment.kt | 38 +++++------ .../confirm}/ConfirmCheckInModule.kt | 2 +- .../confirm/ConfirmCheckInNavigation.kt | 6 ++ .../confirm/ConfirmCheckInViewModel.kt | 22 +++++++ .../scan/ScanCheckInQrCodeFragment.kt | 33 +++++++--- .../scan/ScanCheckInQrCodeModule.kt | 2 +- .../scan/ScanCheckInQrCodeNavigation.kt | 6 ++ .../scan/ScanCheckInQrCodeViewModel.kt | 8 +-- .../checkin/ConfirmCheckInEvent.kt | 6 -- .../checkin/ConfirmCheckInViewModel.kt | 42 ------------ .../scan/ScanCheckInQrCodeEvent.kt | 6 -- .../rki/coronawarnapp/ui/main/MainActivity.kt | 5 +- .../java/de/rki/coronawarnapp/util/Uri.kt | 16 ----- .../src/main/res/drawable/ic_nav_check_in.xml | 9 +++ .../main/res/layout/fragment_check_ins.xml | 37 +++++++++++ ...k_in.xml => fragment_confirm_check_in.xml} | 2 +- .../layout/fragment_scan_check_in_qr_code.xml | 3 +- .../src/main/res/menu/menu_bottom_nav.xml | 5 ++ .../src/main/res/navigation/nav_graph.xml | 22 ++----- .../trace_location_attendee_nav_graph.xml | 41 ++++++++++++ .../trace_location_organizer_nav_graph.xml | 6 ++ .../src/main/res/values-de/strings.xml | 6 ++ .../src/main/res/values/strings.xml | 6 ++ .../confirm/ConfirmCheckInViewModelTest.kt | 35 ++++++++++ .../checkins/qrcode/InvalidUrlProvider.kt | 32 +++++++++ .../checkins/qrcode/UriValidatorTest.kt | 29 +++++++++ .../checkins/qrcode/ValidUrlProvider.kt | 23 +++++++ .../scan/ScanCheckInQrCodeViewModelTest.kt | 8 +-- .../checkin/ConfirmCheckInViewModelTest.kt | 39 ----------- 46 files changed, 569 insertions(+), 242 deletions(-) create mode 100644 Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/VerifiedTraceLocationKtTest.kt delete mode 100644 Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/util/UriTest.kt delete mode 100644 Corona-Warn-App/src/deviceForTesters/res/values/strings.xml create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/UriValidator.kt create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/CheckInsFragment.kt create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/CheckInsModule.kt create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/CheckInsViewModel.kt create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/VerifiedTraceLocation.kt rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/{checkin => attendee/confirm}/ConfirmCheckInFragment.kt (53%) rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/{checkin => attendee/confirm}/ConfirmCheckInModule.kt (88%) create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInNavigation.kt create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/{ => attendee}/scan/ScanCheckInQrCodeFragment.kt (82%) rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/{ => attendee}/scan/ScanCheckInQrCodeModule.kt (89%) create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeNavigation.kt rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/{ => attendee}/scan/ScanCheckInQrCodeViewModel.kt (70%) delete mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInEvent.kt delete mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInViewModel.kt delete mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeEvent.kt delete mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/Uri.kt create mode 100644 Corona-Warn-App/src/main/res/drawable/ic_nav_check_in.xml create mode 100644 Corona-Warn-App/src/main/res/layout/fragment_check_ins.xml rename Corona-Warn-App/src/main/res/layout/{fragment_confrim_check_in.xml => fragment_confirm_check_in.xml} (96%) create mode 100644 Corona-Warn-App/src/main/res/navigation/trace_location_attendee_nav_graph.xml create mode 100644 Corona-Warn-App/src/main/res/navigation/trace_location_organizer_nav_graph.xml create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/attendee/confirm/ConfirmCheckInViewModelTest.kt create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/InvalidUrlProvider.kt create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/UriValidatorTest.kt create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt rename Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/{ => attendee}/scan/ScanCheckInQrCodeViewModelTest.kt (77%) diff --git a/Corona-Warn-App/build.gradle b/Corona-Warn-App/build.gradle index dff24f01a..fe6962784 100644 --- a/Corona-Warn-App/build.gradle +++ b/Corona-Warn-App/build.gradle @@ -307,7 +307,7 @@ dependencies { def nav_version = "2.3.3" implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.core:core-ktx:1.3.2' - implementation 'com.google.android.material:material:1.2.1' + implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version" diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/VerifiedTraceLocationKtTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/VerifiedTraceLocationKtTest.kt new file mode 100644 index 000000000..9ae34bda6 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/VerifiedTraceLocationKtTest.kt @@ -0,0 +1,40 @@ +package de.rki.coronawarnapp.ui.eventregistration.attendee.checkin + +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.QRCodeVerifyResult +import de.rki.coronawarnapp.eventregistration.common.decodeBase32 +import de.rki.coronawarnapp.server.protocols.internal.evreg.SignedEventOuterClass +import io.kotest.assertions.throwables.shouldNotThrowAny +import io.kotest.matchers.shouldBe +import org.joda.time.Instant +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import testhelpers.BaseTestInstrumentation + +@RunWith(JUnit4::class) +class VerifiedTraceLocationKtTest : BaseTestInstrumentation() { + + @Test + fun testVerifiedTraceLocationMapping() { + shouldNotThrowAny { + val signedTraceLocation = + SignedEventOuterClass.SignedEvent.parseFrom(DECODED_TRACE_LOCATION.decodeBase32().toByteArray()) + val verifiedTraceLocation = + QRCodeVerifyResult(singedTraceLocation = signedTraceLocation).toVerifiedTraceLocation() + verifiedTraceLocation shouldBe VerifiedTraceLocation( + guid = "Yc48RFi/hfyXKlF4DEDs/w==", + start = Instant.parse("1970-02-01T02:39:15.000Z"), + end = Instant.parse("1970-02-01T02:39:51.000Z"), + defaultCheckInLengthInMinutes = 30, + description = "CWA Launch Party" + ) + } + } + + companion object { + private const val DECODED_TRACE_LOCATION = + "BIYAUEDBZY6EIWF7QX6JOKSRPAGEB3H7CIIEGV2BEBGGC5LOMNUCAUDBOJ2HSGGTQ6SACIHXQ6SAC" + + "KA6CJEDARQCEEAPHGEZ5JI2K2T422L5U3SMZY5DGCPUZ2RQACAYEJ3HQYMAFFBU2SQCEEAJAUCJSQJ7WDM6" + + "75MCMOD3L2UL7ECJU7TYERH23B746RQTABO3CTI=" + } +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityTest.kt index d21f63c72..9d6924d1d 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityTest.kt @@ -59,13 +59,7 @@ class LauncherActivityTest : BaseUITest() { @Test fun testDeepLinkLowercase() { - val uri = Uri.parse("https://coronawarn.app/E1/SOME_PATH_GOES_HERE") - launchActivity<LauncherActivity>(getIntent(uri)) - } - - @Test - fun testDeepLinkLowercaseWww() { - val uri = Uri.parse("https://www.coronawarn.app/E1/SOME_PATH_GOES_HERE") + val uri = Uri.parse("https://e.coronawarn.app/c1/SOME_PATH_GOES_HERE") launchActivity<LauncherActivity>(getIntent(uri)) } diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/util/UriTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/util/UriTest.kt deleted file mode 100644 index 300fc6c32..000000000 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/util/UriTest.kt +++ /dev/null @@ -1,45 +0,0 @@ -package de.rki.coronawarnapp.util - -import androidx.core.net.toUri -import io.kotest.matchers.shouldBe -import org.junit.Test -import testhelpers.BaseTestInstrumentation - -class UriTest : BaseTestInstrumentation() { - - @Test - fun navUriConvertsSchemeAndAuthorityToLowercase() { - val uri = "HTTPS://CORONAWARN.APP/E1/SOME_PATH_GOES_HERE".toUri() - uri.navUri.toString() shouldBe "https://coronawarn.app/E1/SOME_PATH_GOES_HERE" - - val uri2 = "HTTPS://CORONAWARN.APP/e1/some_path_goes_here".toUri() - uri2.navUri.toString() shouldBe "https://coronawarn.app/e1/some_path_goes_here" - } - - @Test - fun navUriDoesNotChangePath() { - val uri = "https://coronawarn.app/E1/SOME_PATH_GOES_HERE".toUri() - uri.navUri.toString() shouldBe "https://coronawarn.app/E1/SOME_PATH_GOES_HERE" - - val uri2 = "https://coronawarn.app/e1/some_path_goes_here".toUri() - uri2.navUri.toString() shouldBe "https://coronawarn.app/e1/some_path_goes_here" - } - - @Test - fun navUriConvertsSchemeAndAuthorityToLowercaseWithWWW() { - val uri = "HTTPS://WWW.CORONAWARN.APP/E1/SOME_PATH_GOES_HERE".toUri() - uri.navUri.toString() shouldBe "https://www.coronawarn.app/E1/SOME_PATH_GOES_HERE" - - val uri2 = "HTTPS://WWW.CORONAWARN.APP/e1/some_path_goes_here".toUri() - uri2.navUri.toString() shouldBe "https://www.coronawarn.app/e1/some_path_goes_here" - } - - @Test - fun navUriDoesNotChangePathWithWWW() { - val uri = "https://www.coronawarn.app/E1/SOME_PATH_GOES_HERE".toUri() - uri.navUri.toString() shouldBe "https://www.coronawarn.app/E1/SOME_PATH_GOES_HERE" - - val uri2 = "https://www.coronawarn.app/e1/some_path_goes_here".toUri() - uri2.navUri.toString() shouldBe "https://www.coronawarn.app/e1/some_path_goes_here" - } -} diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/qrcode/QrCodeCreationTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/qrcode/QrCodeCreationTestFragment.kt index 4ee387159..2ae2fafd1 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/qrcode/QrCodeCreationTestFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/eventregistration/ui/qrcode/QrCodeCreationTestFragment.kt @@ -1,5 +1,6 @@ package de.rki.coronawarnapp.test.eventregistration.ui.qrcode +import android.annotation.SuppressLint import android.os.Bundle import android.view.View import android.widget.Toast @@ -21,6 +22,7 @@ class QrCodeCreationTestFragment : Fragment(R.layout.fragment_test_qrcode_creati private val viewModel: QrCodeCreationTestViewModel by cwaViewModels { viewModelFactory } private val binding: FragmentTestQrcodeCreationBinding by viewBindingLazy() + @SuppressLint("SetTextI18n") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewModel.sharingIntent.observe2(this) { @@ -36,6 +38,11 @@ class QrCodeCreationTestFragment : Fragment(R.layout.fragment_test_qrcode_creati Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() } + binding.qrCodeText.setText( + "HTTPS://E.CORONAWARN.APP/C1/BIYAUEDBZY6EIWF7QX6JOKSRPAGEB3H7CIIEGV2BEBGGC5LOMNUCAUD" + + "BOJ2HSGGTQ6SACIHXQ6SACKA6CJEDARQCEEAPHGEZ5JI2K2T422L5U3SMZY5DGCPUZ2RQACAYEJ3HQYMAFF" + + "BU2SQCEEAJAUCJSQJ7WDM675MCMOD3L2UL7ECJU7TYERH23B746RQTABO3CTI=" + ) binding.generateQrCode.setOnClickListener { viewModel.createQrCode(binding.qrCodeText.text.toString()) } diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_qrcode_creation.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_qrcode_creation.xml index 915142f41..e6ec6eebc 100644 --- a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_qrcode_creation.xml +++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_qrcode_creation.xml @@ -47,7 +47,6 @@ android:id="@+id/qrCodeText" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/qr_code_input" android:textSize="14sp" /> </com.google.android.material.textfield.TextInputLayout> 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 f3101f22b..2793401c8 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 @@ -148,7 +148,7 @@ tools:layout="@layout/fragment_test_qrcode_creation" /> <fragment android:id="@+id/scanCheckInQrCodeFragmentTest" - android:name="de.rki.coronawarnapp.ui.eventregistration.scan.ScanCheckInQrCodeFragment" + android:name="de.rki.coronawarnapp.ui.eventregistration.attendee.scan.ScanCheckInQrCodeFragment" android:label="ScanCheckInQrCodeFragment" tools:layout="@layout/fragment_submission_qr_code_scan" /> diff --git a/Corona-Warn-App/src/deviceForTesters/res/values/strings.xml b/Corona-Warn-App/src/deviceForTesters/res/values/strings.xml deleted file mode 100644 index 414ff9334..000000000 --- a/Corona-Warn-App/src/deviceForTesters/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <string name="qr_code_input" translatable="false">"HTTPS://CORONAWARN.APP/E1/BIYAUEDBZY6EIWF7QX6JOKSRPAGEB3H7CIIEGV2BEBGGC5LOMNUCAUDBOJ2HSGGTQ6SACIHXQ6SACKA6CJEDARQCEEAPHGEZ5JI2K2T422L5U3SMZY5DGCPUZ2RQACAYEJ3HQYMAFFBU2SQCEEAJAUCJSQJ7WDM675MCMOD3L2UL7ECJU7TYERH23B746RQTABO3CTI="</string> -</resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/AndroidManifest.xml b/Corona-Warn-App/src/main/AndroidManifest.xml index e70468167..2a68d8aca 100644 --- a/Corona-Warn-App/src/main/AndroidManifest.xml +++ b/Corona-Warn-App/src/main/AndroidManifest.xml @@ -69,12 +69,7 @@ <category android:name="android.intent.category.BROWSABLE" /> <data - android:host="coronawarn.app" - android:pathPrefix="/" - android:scheme="https" /> - - <data - android:host="www.coronawarn.app" + android:host="e.coronawarn.app" android:pathPrefix="/" android:scheme="https" /> </intent-filter> diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/EventRegistrationModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/EventRegistrationModule.kt index 93780e4dd..65ab558e1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/EventRegistrationModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/EventRegistrationModule.kt @@ -5,7 +5,6 @@ import dagger.Module import de.rki.coronawarnapp.eventregistration.checkins.qrcode.DefaultQRCodeVerifier import de.rki.coronawarnapp.eventregistration.checkins.qrcode.QRCodeVerifier -@Suppress("EmptyClassBlock") @Module abstract class EventRegistrationModule { @Binds diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/UriValidator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/UriValidator.kt new file mode 100644 index 000000000..48af21b14 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/UriValidator.kt @@ -0,0 +1,24 @@ +package de.rki.coronawarnapp.eventregistration.checkins.qrcode + +import java.net.URI + +private const val SCHEME = "https" +private const val AUTHORITY = "e.coronawarn.app" +private const val PATH_PREFIX = "/c1" +private const val SIGNED_TRACE_LOCATION_BASE_32_REGEX = + "^(?:[A-Z2-7]{8})*(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}=)?\$" + +/** + * Validate that QRCode scanned uri matches the following formulas: + * https://e.coronawarn.app/c1/SIGNED_TRACE_LOCATION_BASE32 + * HTTPS://E.CORONAWARN.APP/C1/SIGNED_TRACE_LOCATION_BASE32 + */ +fun String.isValidQRCodeUri(): Boolean = + URI.create(this).run { + scheme.equals(SCHEME, true) && + authority.equals(AUTHORITY, true) && + path.substringBeforeLast("/") + .equals(PATH_PREFIX, true) && + path.substringAfterLast("/") + .matches(Regex(SIGNED_TRACE_LOCATION_BASE_32_REGEX)) + } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/UIExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/UIExtensions.kt index 3863ee801..4a8dea0d8 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/UIExtensions.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/UIExtensions.kt @@ -54,6 +54,7 @@ fun BottomNavigationView.setupWithNavController2( // For destinations that always show the bottom bar val inShowList = destination.id in listOf( R.id.mainFragment, + R.id.checkInsFragment, R.id.contactDiaryOverviewFragment ) // For destinations that can show or hide the bottom bar in different cases diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/EventRegistrationUIModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/EventRegistrationUIModule.kt index e4e97fea4..a87ab34f4 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/EventRegistrationUIModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/EventRegistrationUIModule.kt @@ -2,10 +2,12 @@ package de.rki.coronawarnapp.ui.eventregistration import dagger.Module import dagger.android.ContributesAndroidInjector -import de.rki.coronawarnapp.ui.eventregistration.checkin.ConfirmCheckInFragment -import de.rki.coronawarnapp.ui.eventregistration.checkin.ConfirmCheckInModule -import de.rki.coronawarnapp.ui.eventregistration.scan.ScanCheckInQrCodeFragment -import de.rki.coronawarnapp.ui.eventregistration.scan.ScanCheckInQrCodeModule +import de.rki.coronawarnapp.ui.eventregistration.attendee.checkin.CheckInsFragment +import de.rki.coronawarnapp.ui.eventregistration.attendee.checkin.CheckInsModule +import de.rki.coronawarnapp.ui.eventregistration.attendee.confirm.ConfirmCheckInFragment +import de.rki.coronawarnapp.ui.eventregistration.attendee.confirm.ConfirmCheckInModule +import de.rki.coronawarnapp.ui.eventregistration.attendee.scan.ScanCheckInQrCodeFragment +import de.rki.coronawarnapp.ui.eventregistration.attendee.scan.ScanCheckInQrCodeModule @Module internal abstract class EventRegistrationUIModule { @@ -15,4 +17,7 @@ internal abstract class EventRegistrationUIModule { @ContributesAndroidInjector(modules = [ConfirmCheckInModule::class]) abstract fun confirmCheckInFragment(): ConfirmCheckInFragment + + @ContributesAndroidInjector(modules = [CheckInsModule::class]) + abstract fun checkInsFragment(): CheckInsFragment } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/CheckInsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/CheckInsFragment.kt new file mode 100644 index 000000000..2ab0eb9fa --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/CheckInsFragment.kt @@ -0,0 +1,65 @@ +package de.rki.coronawarnapp.ui.eventregistration.attendee.checkin + +import android.net.Uri +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.View +import androidx.core.net.toUri +import androidx.navigation.fragment.FragmentNavigatorExtras +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs +import com.google.android.material.transition.Hold +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.databinding.FragmentCheckInsBinding +import de.rki.coronawarnapp.util.di.AutoInject +import de.rki.coronawarnapp.util.ui.doNavigate +import de.rki.coronawarnapp.util.ui.observe2 +import de.rki.coronawarnapp.util.ui.viewBindingLazy +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider +import de.rki.coronawarnapp.util.viewmodel.cwaViewModels +import javax.inject.Inject + +class CheckInsFragment : Fragment(R.layout.fragment_check_ins), AutoInject { + + @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory + private val viewModel: CheckInsViewModel by cwaViewModels { viewModelFactory } + private val binding: FragmentCheckInsBinding by viewBindingLazy() + + // Encoded uri is a one-time use data and then cleared + private val uri: String? + get() = navArgs<CheckInsFragmentArgs>().value + .uri + .also { arguments?.clear() } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + exitTransition = Hold() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + with(binding.scanCheckinQrcodeFab) { + setOnClickListener { + findNavController().navigate( + R.id.action_checkInsFragment_to_scanCheckInQrCodeFragment, + null, + null, + FragmentNavigatorExtras(this to transitionName) + ) + } + } + + uri?.let { viewModel.verifyUri(it) } + viewModel.verifyResult.observe2(this) { + doNavigate( + CheckInsFragmentDirections + .actionCheckInsFragmentToConfirmCheckInFragment(it.toVerifiedTraceLocation()) + ) + } + } + + companion object { + fun uri(rootUri: String): Uri = "coronawarnapp://check-ins/$rootUri".toUri() + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/CheckInsModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/CheckInsModule.kt new file mode 100644 index 000000000..d4b68e13e --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/CheckInsModule.kt @@ -0,0 +1,18 @@ +package de.rki.coronawarnapp.ui.eventregistration.attendee.checkin + +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey + +@Module +abstract class CheckInsModule { + @Binds + @IntoMap + @CWAViewModelKey(CheckInsViewModel::class) + abstract fun checkInsFragment( + factory: CheckInsViewModel.Factory + ): CWAViewModelFactory<out CWAViewModel> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/CheckInsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/CheckInsViewModel.kt new file mode 100644 index 000000000..c72f034e3 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/CheckInsViewModel.kt @@ -0,0 +1,43 @@ +package de.rki.coronawarnapp.ui.eventregistration.attendee.checkin + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.QRCodeVerifier +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.QRCodeVerifyResult +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.isValidQRCodeUri +import de.rki.coronawarnapp.exception.ExceptionCategory +import de.rki.coronawarnapp.exception.reporting.report +import de.rki.coronawarnapp.util.coroutine.DispatcherProvider +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory +import timber.log.Timber + +class CheckInsViewModel @AssistedInject constructor( + dispatcherProvider: DispatcherProvider, + private val qrCodeVerifier: QRCodeVerifier +) : CWAViewModel(dispatcherProvider) { + + private val verifyResultData = MutableLiveData<QRCodeVerifyResult>() + val verifyResult: LiveData<QRCodeVerifyResult> = verifyResultData + + fun verifyUri(uri: String) = launch { + try { + Timber.i("uri: $uri") + if (!uri.isValidQRCodeUri()) + throw IllegalArgumentException("Invalid uri: $uri") + + val encodedEvent = uri.substringAfterLast("/") + val verifyResult = qrCodeVerifier.verify(encodedEvent) + Timber.i("verifyResult: $verifyResult") + verifyResultData.postValue(verifyResult) + } catch (e: Exception) { + Timber.d(e, "TraceLocation verification failed") + e.report(ExceptionCategory.INTERNAL) + } + } + + @AssistedFactory + interface Factory : SimpleCWAViewModelFactory<CheckInsViewModel> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/VerifiedTraceLocation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/VerifiedTraceLocation.kt new file mode 100644 index 000000000..b733005d9 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkin/VerifiedTraceLocation.kt @@ -0,0 +1,35 @@ +package de.rki.coronawarnapp.ui.eventregistration.attendee.checkin + +import android.os.Parcelable +import de.rki.coronawarnapp.eventregistration.checkins.qrcode.QRCodeVerifyResult +import kotlinx.parcelize.Parcelize +import okio.ByteString.Companion.toByteString +import org.joda.time.Instant +import java.util.concurrent.TimeUnit + +@Parcelize +data class VerifiedTraceLocation( + val guid: String, + val description: String?, + val start: Instant?, + val end: Instant?, + val defaultCheckInLengthInMinutes: Int, + // TODO add required properties to confirm check-in +) : Parcelable + +fun QRCodeVerifyResult.toVerifiedTraceLocation() = + with(singedTraceLocation.event) { + VerifiedTraceLocation( + guid = guid.toByteArray().toByteString().base64(), + start = start.instant(), + end = end.instant(), + description = description, + defaultCheckInLengthInMinutes = defaultCheckInLengthInMinutes + ) + } + +/** + * Converts time in seconds into [Instant] + */ +private fun Int.instant() = + if (this == 0) null else Instant.ofEpochMilli(TimeUnit.SECONDS.toMillis(toLong())) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInFragment.kt similarity index 53% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInFragment.kt index cb50a2092..804b57eb9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInFragment.kt @@ -1,11 +1,11 @@ -package de.rki.coronawarnapp.ui.eventregistration.checkin +package de.rki.coronawarnapp.ui.eventregistration.attendee.confirm import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment import androidx.navigation.fragment.navArgs import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.databinding.FragmentConfrimCheckInBinding +import de.rki.coronawarnapp.databinding.FragmentConfirmCheckInBinding import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.ui.observe2 import de.rki.coronawarnapp.util.ui.popBackStack @@ -14,12 +14,12 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModels import javax.inject.Inject -class ConfirmCheckInFragment : Fragment(R.layout.fragment_confrim_check_in), AutoInject { +class ConfirmCheckInFragment : Fragment(R.layout.fragment_confirm_check_in), AutoInject { @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory - private val viewModel: ConfirmCheckInViewModel by cwaViewModels { viewModelFactory } - private val binding: FragmentConfrimCheckInBinding by viewBindingLazy() + private val viewModel: ConfirmCheckInViewModel by cwaViewModels { viewModelFactory } + private val binding: FragmentConfirmCheckInBinding by viewBindingLazy() private val args by navArgs<ConfirmCheckInFragmentArgs>() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -27,25 +27,21 @@ class ConfirmCheckInFragment : Fragment(R.layout.fragment_confrim_check_in), Aut with(binding) { toolbar.setNavigationOnClickListener { viewModel.onClose() } - confirmButton.setOnClickListener { viewModel.onConfirmEvent() } + confirmButton.setOnClickListener { viewModel.onConfirmTraceLocation() } + // TODO bind final UI + eventGuid.text = "GUID: %s".format(args.traceLocation.guid) + startTime.text = "Start time: %s".format(args.traceLocation.start) + endTime.text = "End time: %s".format(args.traceLocation.end) + description.text = "Description: %s".format(args.traceLocation.description) } - viewModel.decodeEvent(args.encodedEvent) - viewModel.navigationEvents.observe2(this) { navEvent -> + viewModel.events.observe2(this) { navEvent -> when (navEvent) { - ConfirmCheckInEvent.BackEvent -> popBackStack() - ConfirmCheckInEvent.ConfirmEvent -> popBackStack() // TODO Do something else - } - } - - // TODO bind data to actual UI - viewModel.verifyResult.observe2(this) { - val event = it.singedTraceLocation.event - with(binding) { - eventGuid.text = "GUID: %s".format(event.guid) - startTime.text = "Start time: %s".format(event.start) - endTime.text = "End time: %s".format(event.end) - description.text = "Description: %s".format(event.description) + ConfirmCheckInNavigation.BackNavigation -> popBackStack() + ConfirmCheckInNavigation.ConfirmNavigation -> { + // TODO Navigate to the rightful destination + popBackStack() + } } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInModule.kt similarity index 88% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInModule.kt index 46d8d5282..fa18d2e2e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.eventregistration.checkin +package de.rki.coronawarnapp.ui.eventregistration.attendee.confirm import dagger.Binds import dagger.Module diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInNavigation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInNavigation.kt new file mode 100644 index 000000000..0620735ed --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInNavigation.kt @@ -0,0 +1,6 @@ +package de.rki.coronawarnapp.ui.eventregistration.attendee.confirm + +sealed class ConfirmCheckInNavigation { + object BackNavigation : ConfirmCheckInNavigation() + object ConfirmNavigation : ConfirmCheckInNavigation() +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt new file mode 100644 index 000000000..b39310160 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/confirm/ConfirmCheckInViewModel.kt @@ -0,0 +1,22 @@ +package de.rki.coronawarnapp.ui.eventregistration.attendee.confirm + +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.util.ui.SingleLiveEvent +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory + +class ConfirmCheckInViewModel @AssistedInject constructor() : CWAViewModel() { + val events = SingleLiveEvent<ConfirmCheckInNavigation>() + + fun onClose() { + events.value = ConfirmCheckInNavigation.BackNavigation + } + + fun onConfirmTraceLocation() { + events.value = ConfirmCheckInNavigation.ConfirmNavigation + } + + @AssistedFactory + interface Factory : SimpleCWAViewModelFactory<ConfirmCheckInViewModel> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeFragment.kt similarity index 82% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeFragment.kt index cdf5b1045..c8d757c66 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeFragment.kt @@ -1,26 +1,28 @@ -package de.rki.coronawarnapp.ui.eventregistration.scan +package de.rki.coronawarnapp.ui.eventregistration.attendee.scan import android.Manifest import android.content.pm.PackageManager import android.os.Bundle -import androidx.fragment.app.Fragment import android.view.View import android.view.accessibility.AccessibilityEvent.TYPE_ANNOUNCEMENT -import androidx.core.net.toUri +import androidx.fragment.app.Fragment +import androidx.navigation.NavOptions import androidx.navigation.fragment.findNavController +import com.google.android.material.transition.MaterialContainerTransform import com.google.zxing.BarcodeFormat import com.journeyapps.barcodescanner.DefaultDecoderFactory import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentScanCheckInQrCodeBinding +import de.rki.coronawarnapp.ui.eventregistration.attendee.checkin.CheckInsFragment import de.rki.coronawarnapp.util.CameraPermissionHelper import de.rki.coronawarnapp.util.DialogHelper import de.rki.coronawarnapp.util.di.AutoInject -import de.rki.coronawarnapp.util.navUri import de.rki.coronawarnapp.util.ui.observe2 import de.rki.coronawarnapp.util.ui.popBackStack import de.rki.coronawarnapp.util.ui.viewBindingLazy import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModels +import timber.log.Timber import javax.inject.Inject class ScanCheckInQrCodeFragment : @@ -33,6 +35,13 @@ class ScanCheckInQrCodeFragment : private val binding: FragmentScanCheckInQrCodeBinding by viewBindingLazy() private var showsPermissionDialog = false + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + sharedElementEnterTransition = MaterialContainerTransform() + sharedElementReturnTransition = MaterialContainerTransform() + } + override fun onViewCreated( view: View, savedInstanceState: Bundle? @@ -46,12 +55,18 @@ class ScanCheckInQrCodeFragment : checkInQrCodeScanViewfinderView.setCameraPreview(binding.checkInQrCodeScanPreview) } - viewModel.navigationEvents.observe2(this) { navEvent -> + viewModel.events.observe2(this) { navEvent -> when (navEvent) { - is ScanCheckInQrCodeEvent.BackEvent -> popBackStack() - is ScanCheckInQrCodeEvent.ConfirmCheckInEvent -> findNavController().navigate( - navEvent.url.toUri().navUri - ) + is ScanCheckInQrCodeNavigation.BackNavigation -> popBackStack() + is ScanCheckInQrCodeNavigation.ScanResultNavigation -> { + Timber.i(navEvent.uri) + findNavController().navigate( + CheckInsFragment.uri(navEvent.uri), + NavOptions.Builder() + .setPopUpTo(R.id.checkInsFragment, true) + .build() + ) + } } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeModule.kt similarity index 89% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeModule.kt index 64d7a0813..37349adb9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.eventregistration.scan +package de.rki.coronawarnapp.ui.eventregistration.attendee.scan import dagger.Binds import dagger.Module diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeNavigation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeNavigation.kt new file mode 100644 index 000000000..db3c562d8 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeNavigation.kt @@ -0,0 +1,6 @@ +package de.rki.coronawarnapp.ui.eventregistration.attendee.scan + +sealed class ScanCheckInQrCodeNavigation { + object BackNavigation : ScanCheckInQrCodeNavigation() + data class ScanResultNavigation(val uri: String) : ScanCheckInQrCodeNavigation() +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeViewModel.kt similarity index 70% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeViewModel.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeViewModel.kt index 22a8d6502..5452829bd 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeViewModel.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.eventregistration.scan +package de.rki.coronawarnapp.ui.eventregistration.attendee.scan import com.journeyapps.barcodescanner.BarcodeResult import dagger.assisted.AssistedFactory @@ -8,14 +8,14 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory class ScanCheckInQrCodeViewModel @AssistedInject constructor() : CWAViewModel() { - val navigationEvents = SingleLiveEvent<ScanCheckInQrCodeEvent>() + val events = SingleLiveEvent<ScanCheckInQrCodeNavigation>() fun onNavigateUp() { - navigationEvents.value = ScanCheckInQrCodeEvent.BackEvent + events.value = ScanCheckInQrCodeNavigation.BackNavigation } fun onScanResult(barcodeResult: BarcodeResult) { - navigationEvents.value = ScanCheckInQrCodeEvent.ConfirmCheckInEvent( + events.value = ScanCheckInQrCodeNavigation.ScanResultNavigation( barcodeResult.result.text ) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInEvent.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInEvent.kt deleted file mode 100644 index 120c3ce22..000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInEvent.kt +++ /dev/null @@ -1,6 +0,0 @@ -package de.rki.coronawarnapp.ui.eventregistration.checkin - -sealed class ConfirmCheckInEvent { - object BackEvent : ConfirmCheckInEvent() - object ConfirmEvent : ConfirmCheckInEvent() -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInViewModel.kt deleted file mode 100644 index 6708ebe00..000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInViewModel.kt +++ /dev/null @@ -1,42 +0,0 @@ -package de.rki.coronawarnapp.ui.eventregistration.checkin - -import androidx.lifecycle.MutableLiveData -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject -import de.rki.coronawarnapp.eventregistration.checkins.qrcode.QRCodeVerifier -import de.rki.coronawarnapp.eventregistration.checkins.qrcode.QRCodeVerifyResult -import de.rki.coronawarnapp.exception.ExceptionCategory -import de.rki.coronawarnapp.exception.reporting.report -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 ConfirmCheckInViewModel @AssistedInject constructor( - private val qrCodeVerifier: QRCodeVerifier -) : CWAViewModel() { - private val internalVerifyResult = MutableLiveData<QRCodeVerifyResult>() - val verifyResult = internalVerifyResult - val navigationEvents = SingleLiveEvent<ConfirmCheckInEvent>() - - fun decodeEvent(encodedEvent: String) = launch { - // TODO this logic should moved from here. Here user should confirm event only - try { - internalVerifyResult.postValue(qrCodeVerifier.verify(encodedEvent)) - } catch (e: Exception) { - Timber.d(e) - e.report(ExceptionCategory.INTERNAL) - } - } - - fun onClose() { - navigationEvents.value = ConfirmCheckInEvent.BackEvent - } - - fun onConfirmEvent() { - navigationEvents.value = ConfirmCheckInEvent.ConfirmEvent - } - - @AssistedFactory - interface Factory : SimpleCWAViewModelFactory<ConfirmCheckInViewModel> -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeEvent.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeEvent.kt deleted file mode 100644 index d833a241f..000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeEvent.kt +++ /dev/null @@ -1,6 +0,0 @@ -package de.rki.coronawarnapp.ui.eventregistration.scan - -sealed class ScanCheckInQrCodeEvent { - object BackEvent : ScanCheckInQrCodeEvent() - data class ConfirmCheckInEvent(val url: String) : ScanCheckInQrCodeEvent() -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt index 4c5cc0b68..88b36e0a7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt @@ -24,6 +24,7 @@ import de.rki.coronawarnapp.datadonation.analytics.worker.DataDonationAnalyticsS import de.rki.coronawarnapp.deadman.DeadmanNotificationScheduler import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.ui.base.startActivitySafely +import de.rki.coronawarnapp.ui.eventregistration.attendee.checkin.CheckInsFragment import de.rki.coronawarnapp.ui.setupWithNavController2 import de.rki.coronawarnapp.util.AppShortcuts import de.rki.coronawarnapp.util.CWADebug @@ -31,7 +32,6 @@ import de.rki.coronawarnapp.util.ConnectivityHelper import de.rki.coronawarnapp.util.DialogHelper import de.rki.coronawarnapp.util.device.PowerManagement import de.rki.coronawarnapp.util.di.AppInjector -import de.rki.coronawarnapp.util.navUri import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper.Companion.getShortcutExtra import de.rki.coronawarnapp.util.ui.findNavController import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider @@ -156,8 +156,7 @@ class MainActivity : AppCompatActivity(), HasAndroidInjector { private fun navigateByIntentUri(intent: Intent?) { val uri = intent?.data ?: return Timber.i("Uri:$uri") - Timber.i("NavUri:%s", uri.navUri) - navController.navigate(uri.navUri) + navController.navigate(CheckInsFragment.uri(uri.toString())) } /** diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/Uri.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/Uri.kt deleted file mode 100644 index 93ab36f1c..000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/Uri.kt +++ /dev/null @@ -1,16 +0,0 @@ -package de.rki.coronawarnapp.util - -import android.net.Uri -import androidx.navigation.NavController -import java.util.Locale - -/** - * [NavController.navigate] by Uri is case sensitive. When authority and/or scheme are - * in Uppercase letter an Exception will thrown. - * To avoid such cases [navUri] is converting Uri schema and authority to lowercase always. - */ -val Uri.navUri: Uri - get() = Uri.Builder() - .authority(authority?.toLowerCase(Locale.ROOT)) - .scheme(scheme?.toLowerCase(Locale.ROOT)) - .path(path).build() diff --git a/Corona-Warn-App/src/main/res/drawable/ic_nav_check_in.xml b/Corona-Warn-App/src/main/res/drawable/ic_nav_check_in.xml new file mode 100644 index 000000000..a0597989a --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_nav_check_in.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="23dp" + android:height="22dp" + android:viewportWidth="23" + android:viewportHeight="22"> + <path + android:pathData="M21.1465,7.581C21.7158,7.581 22.0166,7.2588 22.0166,6.7002V4.0684C22.0166,1.8555 20.8887,0.749 18.6436,0.749H16.0117C15.4531,0.749 15.1416,1.0498 15.1416,1.6084C15.1416,2.167 15.4531,2.4785 16.0117,2.4785H18.6113C19.6748,2.4785 20.2871,3.0478 20.2871,4.165V6.7002C20.2871,7.2588 20.5986,7.581 21.1465,7.581ZM1.8428,7.581C2.4121,7.581 2.7129,7.2588 2.7129,6.7002V4.165C2.7129,3.0478 3.3037,2.4785 4.3779,2.4785H6.9775C7.5469,2.4785 7.8584,2.167 7.8584,1.6084C7.8584,1.0498 7.5469,0.749 6.9775,0.749H4.3565C2.1113,0.749 0.9834,1.8555 0.9834,4.0684V6.7002C0.9834,7.2588 1.2949,7.581 1.8428,7.581ZM7.3106,17.0771H15.668C16.4199,17.0771 16.7637,16.7871 16.7637,16.0352V6.4746C16.7637,5.7227 16.4199,5.4326 15.668,5.4326H7.3106C6.5586,5.4326 6.2148,5.7227 6.2148,6.4746V16.0352C6.2148,16.7871 6.5586,17.0771 7.3106,17.0771ZM9.169,9.3857C8.8145,9.3857 8.5889,9.1494 8.5889,8.8164C8.5889,8.4619 8.8145,8.2256 9.169,8.2256H13.8203C14.1748,8.2256 14.4111,8.4619 14.4111,8.8164C14.4111,9.1494 14.1748,9.3857 13.8203,9.3857H9.169ZM9.169,11.6953C8.8145,11.6953 8.5889,11.4482 8.5889,11.1152C8.5889,10.7715 8.8145,10.5244 9.169,10.5244H11.7363C12.0801,10.5244 12.3164,10.7715 12.3164,11.1152C12.3164,11.4482 12.0801,11.6953 11.7363,11.6953H9.169ZM4.3565,21.7715H6.9775C7.5469,21.7715 7.8584,21.46 7.8584,20.9121C7.8584,20.3535 7.5469,20.042 6.9775,20.042H4.3779C3.3037,20.042 2.7129,19.4727 2.7129,18.3555V15.8203C2.7129,15.251 2.4014,14.9395 1.8428,14.9395C1.2842,14.9395 0.9834,15.251 0.9834,15.8203V18.4414C0.9834,20.665 2.1113,21.7715 4.3565,21.7715ZM16.0117,21.7715H18.6436C20.8887,21.7715 22.0166,20.6543 22.0166,18.4414V15.8203C22.0166,15.251 21.7051,14.9395 21.1465,14.9395C20.5879,14.9395 20.2871,15.251 20.2871,15.8203V18.3555C20.2871,19.4727 19.6748,20.042 18.6113,20.042H16.0117C15.4531,20.042 15.1416,20.3535 15.1416,20.9121C15.1416,21.46 15.4531,21.7715 16.0117,21.7715Z" + android:fillColor="#949494"/> +</vector> diff --git a/Corona-Warn-App/src/main/res/layout/fragment_check_ins.xml b/Corona-Warn-App/src/main/res/layout/fragment_check_ins.xml new file mode 100644 index 000000000..1053da359 --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/fragment_check_ins.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/content_container" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <com.google.android.material.appbar.MaterialToolbar + android:id="@+id/toolbar" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/check_ins_recycler" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/toolbar" /> + + <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton + android:id="@+id/scan_checkin_qrcode_fab" + style="@style/Widget.App.ExtendedFloatingActionButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="@dimen/spacing_normal" + android:text="@string/scan_check_in_qr_code" + android:transitionName="shared_element_container" + app:icon="@drawable/ic_nav_check_in" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" /> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/fragment_confrim_check_in.xml b/Corona-Warn-App/src/main/res/layout/fragment_confirm_check_in.xml similarity index 96% rename from Corona-Warn-App/src/main/res/layout/fragment_confrim_check_in.xml rename to Corona-Warn-App/src/main/res/layout/fragment_confirm_check_in.xml index 5f80dbeff..b5b111f19 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_confrim_check_in.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_confirm_check_in.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".ui.eventregistration.checkin.ConfirmCheckInFragment"> + tools:context=".ui.eventregistration.attendee.confirm.ConfirmCheckInFragment"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" diff --git a/Corona-Warn-App/src/main/res/layout/fragment_scan_check_in_qr_code.xml b/Corona-Warn-App/src/main/res/layout/fragment_scan_check_in_qr_code.xml index aa0768d4a..b14d08f71 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_scan_check_in_qr_code.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_scan_check_in_qr_code.xml @@ -4,7 +4,8 @@ android:id="@+id/check_in_qr_code_scan_container" android:layout_width="match_parent" android:layout_height="match_parent" - android:contentDescription="@string/submission_qr_code_scan_title"> + android:contentDescription="@string/submission_qr_code_scan_title" + android:transitionName="shared_element_container"> <com.journeyapps.barcodescanner.BarcodeView android:id="@+id/check_in_qr_code_scan_preview" diff --git a/Corona-Warn-App/src/main/res/menu/menu_bottom_nav.xml b/Corona-Warn-App/src/main/res/menu/menu_bottom_nav.xml index 3c5985250..bef4a123a 100644 --- a/Corona-Warn-App/src/main/res/menu/menu_bottom_nav.xml +++ b/Corona-Warn-App/src/main/res/menu/menu_bottom_nav.xml @@ -6,6 +6,11 @@ android:icon="@drawable/ic_nav_home" android:title="@string/bottom_nav_home_title" /> + <item + android:id="@+id/trace_location_attendee_nav_graph" + android:icon="@drawable/ic_nav_check_in" + android:title="@string/bottom_nav_check_ins_title" /> + <item android:id="@+id/contact_diary_nav_graph" android:icon="@drawable/ic_nav_diary" diff --git a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml index 0fca272a7..cc6efcc8e 100644 --- a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml @@ -9,6 +9,9 @@ <include app:graph="@navigation/test_nav_graph" /> <!-- Contact Diary graph--> <include app:graph="@navigation/contact_diary_nav_graph" /> + <!-- Event registration graphs--> + <include app:graph="@navigation/trace_location_organizer_nav_graph" /> + <include app:graph="@navigation/trace_location_attendee_nav_graph" /> <!-- Main --> <fragment @@ -593,7 +596,7 @@ android:id="@+id/debugLogUploadFragment" android:name="de.rki.coronawarnapp.bugreporting.debuglog.ui.upload.DebugLogUploadFragment" android:label="DebugLogUploadFragment" - tools:layout="@layout/bugreporting_debuglog_upload_fragment" > + tools:layout="@layout/bugreporting_debuglog_upload_fragment"> <action android:id="@+id/action_debugLogUploadFragment_to_debugLogLegalFragment" app:destination="@id/debugLogLegalFragment" /> @@ -603,21 +606,4 @@ android:name="de.rki.coronawarnapp.bugreporting.debuglog.ui.legal.DebugLogLegalFragment" android:label="DebugLogLegalFragment" tools:layout="@layout/bugreporting_legal_fragment" /> - - <fragment - android:id="@+id/verifyEventFragment" - android:name="de.rki.coronawarnapp.ui.eventregistration.checkin.ConfirmCheckInFragment" - android:label="fragment_verify_event" - tools:layout="@layout/fragment_confrim_check_in"> - <deepLink app:uri="https://coronawarn.app/E1/{encodedEvent}" /> - <argument - android:name="encodedEvent" - app:argType="string" /> - - </fragment> - <fragment - android:id="@+id/scanCheckInQrCodeFragment" - android:name="de.rki.coronawarnapp.ui.eventregistration.scan.ScanCheckInQrCodeFragment" - android:label="ScanCheckInQrCodeFragment" - tools:layout="@layout/fragment_scan_check_in_qr_code" /> </navigation> diff --git a/Corona-Warn-App/src/main/res/navigation/trace_location_attendee_nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/trace_location_attendee_nav_graph.xml new file mode 100644 index 000000000..062c8064c --- /dev/null +++ b/Corona-Warn-App/src/main/res/navigation/trace_location_attendee_nav_graph.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<navigation xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/trace_location_attendee_nav_graph" + app:startDestination="@id/checkInsFragment"> + <fragment + android:id="@+id/confirmCheckInFragment" + android:name="de.rki.coronawarnapp.ui.eventregistration.attendee.confirm.ConfirmCheckInFragment" + android:label="fragment_confirm_check_in" + tools:layout="@layout/fragment_confirm_check_in"> + <argument + android:name="traceLocation" + app:argType="de.rki.coronawarnapp.ui.eventregistration.attendee.checkin.VerifiedTraceLocation" /> + </fragment> + <fragment + android:id="@+id/scanCheckInQrCodeFragment" + android:name="de.rki.coronawarnapp.ui.eventregistration.attendee.scan.ScanCheckInQrCodeFragment" + android:label="ScanCheckInQrCodeFragment" + tools:layout="@layout/fragment_scan_check_in_qr_code" /> + + <fragment + android:id="@+id/checkInsFragment" + android:name="de.rki.coronawarnapp.ui.eventregistration.attendee.checkin.CheckInsFragment" + android:label="CheckInsFragment" + tools:layout="@layout/fragment_check_ins"> + <deepLink app:uri="coronawarnapp://check-ins/{uri}" /> + <action + android:id="@+id/action_checkInsFragment_to_scanCheckInQrCodeFragment" + app:destination="@id/scanCheckInQrCodeFragment" /> + <action + android:id="@+id/action_checkInsFragment_to_confirmCheckInFragment" + app:destination="@id/confirmCheckInFragment" /> + <argument + android:name="uri" + android:defaultValue="@null" + app:argType="string" + app:nullable="true" /> + </fragment> + +</navigation> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/navigation/trace_location_organizer_nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/trace_location_organizer_nav_graph.xml new file mode 100644 index 000000000..35744089a --- /dev/null +++ b/Corona-Warn-App/src/main/res/navigation/trace_location_organizer_nav_graph.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<navigation xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/event_organizer_nav_graph"> + <!-- TODO add organiser screens --> +</navigation> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-de/strings.xml b/Corona-Warn-App/src/main/res/values-de/strings.xml index 34c43dc98..d12543303 100644 --- a/Corona-Warn-App/src/main/res/values-de/strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/strings.xml @@ -1839,6 +1839,8 @@ <string name="bottom_nav_home_title">Startseite</string> <!-- XHED: Title for BottomNav diary screen title --> <string name="bottom_nav_diary_title">Tagebuch</string> + <!-- XHED: Title for BottomNav check-in screen title --> + <string name="bottom_nav_check_ins_title">Check In</string> <!-- #################################### Data Donation & Survey @@ -1956,4 +1958,8 @@ <string name="duration_dialog_ok_button">OK</string> <!-- NOTR --> <string name="duration_dialog_default_value">00:00</string> + + <!-- Scan check in QR Code--> + <!-- XTXT: Scan check in QR-Code FAB text--> + <string name="scan_check_in_qr_code">QR-Code scannen</string> </resources> diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml index cfa3af545..424c7c16a 100644 --- a/Corona-Warn-App/src/main/res/values/strings.xml +++ b/Corona-Warn-App/src/main/res/values/strings.xml @@ -1857,6 +1857,8 @@ <string name="bottom_nav_home_title">"Start Screen"</string> <!-- XHED: Title for BottomNav diary screen title --> <string name="bottom_nav_diary_title">"Journal"</string> + <!-- XHED: Title for BottomNav check-in screen title --> + <string name="bottom_nav_check_ins_title">Check In</string> <!-- #################################### Data Donation & Survey @@ -1974,4 +1976,8 @@ <string name="duration_dialog_ok_button">"OK"</string> <!-- NOTR --> <string name="duration_dialog_default_value">"00:00"</string> + + <!-- Scan check in QR Code--> + <!-- XTXT: Scan check in QR-Code FAB text--> + <string name="scan_check_in_qr_code">Scan QR-Code</string> </resources> diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/attendee/confirm/ConfirmCheckInViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/attendee/confirm/ConfirmCheckInViewModelTest.kt new file mode 100644 index 000000000..1bb9410f2 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/attendee/confirm/ConfirmCheckInViewModelTest.kt @@ -0,0 +1,35 @@ +package de.rki.coronawarnapp.eventregistration.attendee.confirm + +import de.rki.coronawarnapp.ui.eventregistration.attendee.confirm.ConfirmCheckInNavigation +import de.rki.coronawarnapp.ui.eventregistration.attendee.confirm.ConfirmCheckInViewModel +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +import org.junit.jupiter.api.extension.ExtendWith +import testhelpers.BaseTest +import testhelpers.extensions.InstantExecutorExtension +import testhelpers.extensions.getOrAwaitValue + +@ExtendWith(InstantExecutorExtension::class) +class ConfirmCheckInViewModelTest : BaseTest() { + + private lateinit var viewModel: ConfirmCheckInViewModel + + @BeforeEach + fun setUp() { + viewModel = ConfirmCheckInViewModel() + } + + @Test + fun onClose() { + viewModel.onClose() + viewModel.events.getOrAwaitValue() shouldBe ConfirmCheckInNavigation.BackNavigation + } + + @Test + fun onConfirmEvent() { + viewModel.onConfirmTraceLocation() + viewModel.events.getOrAwaitValue() shouldBe ConfirmCheckInNavigation.ConfirmNavigation + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/InvalidUrlProvider.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/InvalidUrlProvider.kt new file mode 100644 index 000000000..62e334191 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/InvalidUrlProvider.kt @@ -0,0 +1,32 @@ +package de.rki.coronawarnapp.eventregistration.checkins.qrcode + +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.ArgumentsProvider +import java.util.stream.Stream + +class InvalidUrlProvider : ArgumentsProvider { + override fun provideArguments(context: ExtensionContext?): Stream<out Arguments> { + return Stream.of( + Arguments.of( + "https://e.coronawarn.app/e1/BIYAUEDBZY6EIWF7QX6JOKSRPAGEB3H7CIIEGV2BEBGGC5LOMNUCAUDBO" + + "J2HSGGTQ6SACIHXQ6SACKA6CJEDARQCEEAPHGEZ5JI2K2T422L5U3SMZY5DGCPUZ2RQACAYEJ3HQYMAFFBU2" + + "SQCEEAJAUCJSQJ7WDM675MCMOD3L2UL7ECJU7TYERH23B746RQTABO3CTI=" + ), + Arguments.of( + "https://e.coronawarn.app/c1/SINGED_ENCODED_LOCATION" + ), + Arguments.of( + "HTTPS://E.CORONAWARN.APP/C1/BIPEY33SMVWSA2LQON2W2IDEN5WG64RAONUXIIDBNVSXILBAMNXRBCM4UQARRKM6UQASAHR" + + "KCC7CTDWGQ4JCO7RVZSWVIMQK4UPA.GBCAEIA7TEORBTUA25QHBOCWT26BCA5PORBS2E4FFWMJ3UU3P6SXOL" + + "7SHUBCA7UEZBDDQ2R6VRJH7WBJKVF7GZYJA6YMRN27IPEP7NKGGJSWX3XQ" + ), + Arguments.of( + "HTTPS://E.CORONAWARN.APP/C1/BIYAUEDBZY6EIWF7QX6JOKSRPAGEB3H7CIIEGV2BEBGGC5LOMNUCAUDBO" + + "J2HSGGTQ6SACIHXQ6SACKA6CJEDARQCEEAPHGEZ5JI2K2T422L5U3SMZY5DGCPUZ2RQACAYEJ3HQYMAFFBU2" + + "SQCEEAJAUCJSQJ7WDM675MCMOD3L2UL7ECJU7TYERH23B746RQTABO3CTI" + ), + Arguments.of("") + ) + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/UriValidatorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/UriValidatorTest.kt new file mode 100644 index 000000000..d0f30a69b --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/UriValidatorTest.kt @@ -0,0 +1,29 @@ +package de.rki.coronawarnapp.eventregistration.checkins.qrcode + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import org.junit.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ArgumentsSource + +class UriValidatorTest { + + @ParameterizedTest + @ArgumentsSource(ValidUrlProvider::class) + fun `Valid URLs`(input: String) { + input.isValidQRCodeUri() shouldBe true + } + + @ParameterizedTest + @ArgumentsSource(InvalidUrlProvider::class) + fun `Invalid URLs`(input: String) { + input.isValidQRCodeUri() shouldBe false + } + + @Test + fun `Invalid URL string`() { + shouldThrow<IllegalArgumentException> { + "Hello World!".isValidQRCodeUri() + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt new file mode 100644 index 000000000..6b8b83fbe --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/eventregistration/checkins/qrcode/ValidUrlProvider.kt @@ -0,0 +1,23 @@ +package de.rki.coronawarnapp.eventregistration.checkins.qrcode + +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.ArgumentsProvider +import java.util.stream.Stream + +class ValidUrlProvider : ArgumentsProvider { + override fun provideArguments(context: ExtensionContext?): Stream<out Arguments> { + return Stream.of( + Arguments.of( + "HTTPS://E.CORONAWARN.APP/C1/BIYAUEDBZY6EIWF7QX6JOKSRPAGEB3H7CIIEGV2BEBGGC5LOMNUCAUDBO" + + "J2HSGGTQ6SACIHXQ6SACKA6CJEDARQCEEAPHGEZ5JI2K2T422L5U3SMZY5DGCPUZ2RQACAYEJ3HQYMAFFBU2" + + "SQCEEAJAUCJSQJ7WDM675MCMOD3L2UL7ECJU7TYERH23B746RQTABO3CTI=" + ), + Arguments.of( + "https://e.coronawarn.app/c1/BIYAUEDBZY6EIWF7QX6JOKSRPAGEB3H7CIIEGV2BEBGGC5LOMNUCAUDBO" + + "J2HSGGTQ6SACIHXQ6SACKA6CJEDARQCEEAPHGEZ5JI2K2T422L5U3SMZY5DGCPUZ2RQACAYEJ3HQYMAFFBU2" + + "SQCEEAJAUCJSQJ7WDM675MCMOD3L2UL7ECJU7TYERH23B746RQTABO3CTI=" + ) + ) + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeViewModelTest.kt similarity index 77% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeViewModelTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeViewModelTest.kt index e855bf055..2dae524bb 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/scan/ScanCheckInQrCodeViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeViewModelTest.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.eventregistration.scan +package de.rki.coronawarnapp.ui.eventregistration.attendee.scan import com.google.zxing.Result import com.journeyapps.barcodescanner.BarcodeResult @@ -25,7 +25,7 @@ class ScanCheckInQrCodeViewModelTest : BaseTest() { @Test fun `onNavigateUp goes back`() { viewModel.onNavigateUp() - viewModel.navigationEvents.getOrAwaitValue() shouldBe ScanCheckInQrCodeEvent.BackEvent + viewModel.events.getOrAwaitValue() shouldBe ScanCheckInQrCodeNavigation.BackNavigation } @Test @@ -36,7 +36,7 @@ class ScanCheckInQrCodeViewModelTest : BaseTest() { } } viewModel.onScanResult(mockedResult) - viewModel.navigationEvents.getOrAwaitValue() shouldBe - ScanCheckInQrCodeEvent.ConfirmCheckInEvent("https://coronawarn.app/E1/SOME_PATH_GOES_HERE") + viewModel.events.getOrAwaitValue() shouldBe + ScanCheckInQrCodeNavigation.ScanResultNavigation("https://coronawarn.app/E1/SOME_PATH_GOES_HERE") } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInViewModelTest.kt index 7b9cc3973..e69de29bb 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/eventregistration/checkin/ConfirmCheckInViewModelTest.kt @@ -1,39 +0,0 @@ -package de.rki.coronawarnapp.ui.eventregistration.checkin - -import de.rki.coronawarnapp.eventregistration.checkins.qrcode.QRCodeVerifier -import io.kotest.matchers.shouldBe -import io.mockk.MockKAnnotations -import io.mockk.impl.annotations.MockK -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -import org.junit.jupiter.api.extension.ExtendWith -import testhelpers.BaseTest -import testhelpers.extensions.InstantExecutorExtension -import testhelpers.extensions.getOrAwaitValue - -@ExtendWith(InstantExecutorExtension::class) -class ConfirmCheckInViewModelTest : BaseTest() { - - private lateinit var viewModel: ConfirmCheckInViewModel - - @MockK lateinit var qrCodeVerifier: QRCodeVerifier - - @BeforeEach - fun setUp() { - MockKAnnotations.init(this) - viewModel = ConfirmCheckInViewModel(qrCodeVerifier) - } - - @Test - fun onClose() { - viewModel.onClose() - viewModel.navigationEvents.getOrAwaitValue() shouldBe ConfirmCheckInEvent.BackEvent - } - - @Test - fun onConfirmEvent() { - viewModel.onConfirmEvent() - viewModel.navigationEvents.getOrAwaitValue() shouldBe ConfirmCheckInEvent.ConfirmEvent - } -} -- GitLab