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