From 47f39c11d168b01cb9e3c9b77a17c0e95ff21cb6 Mon Sep 17 00:00:00 2001
From: Juraj Kusnier <jurajkusnier@users.noreply.github.com>
Date: Tue, 23 Feb 2021 11:25:30 +0100
Subject: [PATCH] Add App launcher shortcut for contact journal
 (EXPOSUREAPP-4659) (#2384)

* Add app shortcut for contact diary (#2130)

* Fix ktlint issue (#2130)

* Fix merge: ContactDiary Activity > Fragment

* Introduce dynamic shortcuts

* Update Test Menu (Onboarding)

* Update Test Menu

* Handle onboarding

* Update strings

* App shortcut improvements

* Code formatting

* Update tests

* Use ShortcutManagerCompat instead of ShortcutManager

* Update contact diary shortcut icon

* Update test menu

* Onboarding refactoring (EXPOSUREAPP-4659) (#2398)

* Move NewRelease and DeltaOnboarding Fragments to OnboardingActivity

* Update method names

* Refresh shortcuts in non UI thread

* Use deeplinks for navigation

* Code cleaning and refactoring

* Update shortcut name after UA Feedback

Co-authored-by: PhilippNowak96 <dev@philipp-nowak.com>
Co-authored-by: Ralf Gehrer <ralfgehrer@users.noreply.github.com>
Co-authored-by: Matthias Urhahn <matthias.urhahn@sap.com>
Co-authored-by: ralfgehrer <mail@ralfgehrer.com>
---
 .../coronawarnapp/ui/main/MainActivityTest.kt |   8 +-
 .../ui/main/home/HomeFragmentTest.kt          |   8 +-
 ...bmissionTestResultAvailableFragmentTest.kt |   5 +
 .../ui/DeltaOnboardingFragmentModule.kt       |  18 +++
 .../ui/DeltaOnboardingFragmentViewModel.kt    |  53 +++++++
 .../ui/DeltaonboardingFragment.kt             |  62 ++++++++
 .../test/menu/ui/TestMenuFragmentViewModel.kt |   4 +-
 .../ui/main/MainActivityTestModule.kt         |   5 +
 .../layout/fragment_test_deltaonboarding.xml  | 150 ++++++++++++++++++
 .../res/navigation/test_nav_graph.xml         |   9 ++
 .../ContactDiaryOnboardingFragment.kt         |  23 ++-
 .../ui/launcher/LauncherActivity.kt           |   6 +-
 .../ui/launcher/LauncherActivityViewModel.kt  |  13 +-
 .../rki/coronawarnapp/ui/main/MainActivity.kt |  56 ++++++-
 .../ui/main/MainActivityActions.kt            |   5 +
 .../ui/main/MainActivityViewModel.kt          |   7 +-
 .../ui/main/home/HomeFragment.kt              |  13 +-
 .../ui/main/home/HomeFragmentEvents.kt        |   3 -
 .../ui/main/home/HomeFragmentViewModel.kt     |  46 +++---
 .../ui/onboarding/OnboardingActivity.kt       |  15 +-
 .../ui/onboarding/OnboardingActivityModule.kt |  19 +++
 ...OnboardingDeltaInteroperabilityFragment.kt |   4 +-
 .../onboarding/OnboardingLoadingFragment.kt   |  53 +++++++
 .../ui/onboarding/OnboardingLoadingModule.kt  |  22 +++
 .../onboarding/OnboardingLoadingViewModel.kt  |  46 ++++++
 .../SubmissionTestResultAvailableFragment.kt  |   3 +
 .../de/rki/coronawarnapp/util/AppShortcuts.kt |   5 +
 .../util/shortcuts/AppShortcutsHelper.kt      |  54 +++++++
 .../ic_contact_diary_shortcut_icon.xml        |  12 ++
 .../res/layout/onboaring_loading_layout.xml   |  15 ++
 .../navigation/contact_diary_nav_graph.xml    |   4 +
 .../src/main/res/navigation/nav_graph.xml     |   6 -
 .../res/navigation/nav_graph_onboarding.xml   |  61 ++++++-
 .../src/main/res/values-de/strings.xml        |   4 +
 .../src/main/res/values/strings.xml           |   3 +
 .../main/home/HomeFragmentViewModelTest.kt    |  15 +-
 .../launcher/LauncherActivityViewModelTest.kt |  12 +-
 37 files changed, 768 insertions(+), 79 deletions(-)
 create mode 100644 Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentModule.kt
 create mode 100644 Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentViewModel.kt
 create mode 100644 Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaonboardingFragment.kt
 create mode 100644 Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_deltaonboarding.xml
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityActions.kt
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingFragment.kt
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingModule.kt
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingViewModel.kt
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/AppShortcuts.kt
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/shortcuts/AppShortcutsHelper.kt
 create mode 100644 Corona-Warn-App/src/main/res/drawable/ic_contact_diary_shortcut_icon.xml
 create mode 100644 Corona-Warn-App/src/main/res/layout/onboaring_loading_layout.xml

diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/MainActivityTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/MainActivityTest.kt
index d457ee7eb..6f2fb1680 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/MainActivityTest.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/MainActivityTest.kt
@@ -48,6 +48,7 @@ import de.rki.coronawarnapp.util.CWADebug
 import de.rki.coronawarnapp.util.device.BackgroundModeStatus
 import de.rki.coronawarnapp.util.device.PowerManagement
 import de.rki.coronawarnapp.util.security.EncryptionErrorResetTool
+import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper
 import de.rki.coronawarnapp.util.ui.SingleLiveEvent
 import de.rki.coronawarnapp.worker.BackgroundWorkScheduler
 import io.mockk.MockKAnnotations
@@ -89,6 +90,7 @@ class MainActivityTest : BaseUITest() {
     @MockK lateinit var appConfigProvider: AppConfigProvider
     @MockK lateinit var statisticsProvider: StatisticsProvider
     @MockK lateinit var deadmanNotificationScheduler: DeadmanNotificationScheduler
+    @MockK lateinit var appShortcutsHelper: AppShortcutsHelper
 
     // MainActivity mocks
     @MockK lateinit var environmentSetup: EnvironmentSetup
@@ -352,7 +354,8 @@ class MainActivityTest : BaseUITest() {
             submissionStateProvider = submissionStateProvider,
             cwaSettings = cwaSettings,
             statisticsProvider = statisticsProvider,
-            deadmanNotificationScheduler = deadmanNotificationScheduler
+            deadmanNotificationScheduler = deadmanNotificationScheduler,
+            appShortcutsHelper = appShortcutsHelper
         )
     )
 
@@ -396,7 +399,8 @@ class MainActivityTest : BaseUITest() {
             every { showLoweredRiskLevelDialog } returns MutableLiveData()
             every { homeItems } returns MutableLiveData(emptyList())
             every { popupEvents } returns SingleLiveEvent()
-            every { showPopUpsOrNavigate() } just Runs
+            every { showPopUps() } just Runs
+            every { restoreAppShortcuts() } just Runs
         }
 
         setupMockViewModel(
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt
index 96c7b1e30..56559565e 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt
@@ -16,6 +16,7 @@ import de.rki.coronawarnapp.tracing.GeneralTracingStatus
 import de.rki.coronawarnapp.tracing.states.TracingStateProvider
 import de.rki.coronawarnapp.tracing.ui.statusbar.TracingHeaderState
 import de.rki.coronawarnapp.util.security.EncryptionErrorResetTool
+import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper
 import de.rki.coronawarnapp.util.ui.SingleLiveEvent
 import io.mockk.MockKAnnotations
 import io.mockk.Runs
@@ -47,6 +48,7 @@ class HomeFragmentTest : BaseUITest() {
     @MockK lateinit var appConfigProvider: AppConfigProvider
     @MockK lateinit var statisticsProvider: StatisticsProvider
     @MockK lateinit var deadmanNotificationScheduler: DeadmanNotificationScheduler
+    @MockK lateinit var appShortcutsHelper: AppShortcutsHelper
 
     private lateinit var viewModel: HomeFragmentViewModel
 
@@ -61,7 +63,8 @@ class HomeFragmentTest : BaseUITest() {
             every { showLoweredRiskLevelDialog } returns MutableLiveData()
             every { homeItems } returns MutableLiveData(emptyList())
             every { popupEvents } returns SingleLiveEvent()
-            every { showPopUpsOrNavigate() } just Runs
+            every { showPopUps() } just Runs
+            every { restoreAppShortcuts() } just Runs
         }
 
         setupMockViewModel(
@@ -96,7 +99,8 @@ class HomeFragmentTest : BaseUITest() {
             submissionStateProvider = submissionStateProvider,
             cwaSettings = cwaSettings,
             statisticsProvider = statisticsProvider,
-            deadmanNotificationScheduler = deadmanNotificationScheduler
+            deadmanNotificationScheduler = deadmanNotificationScheduler,
+            appShortcutsHelper = appShortcutsHelper
         )
     )
 }
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultAvailableFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultAvailableFragmentTest.kt
index a76ab1305..c099702b2 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultAvailableFragmentTest.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultAvailableFragmentTest.kt
@@ -9,9 +9,12 @@ import de.rki.coronawarnapp.submission.auto.AutoSubmission
 import de.rki.coronawarnapp.submission.data.tekhistory.TEKHistoryUpdater_Factory_Impl
 import de.rki.coronawarnapp.ui.submission.resultavailable.SubmissionTestResultAvailableFragment
 import de.rki.coronawarnapp.ui.submission.resultavailable.SubmissionTestResultAvailableViewModel
+import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper
 import io.mockk.MockKAnnotations
+import io.mockk.Runs
 import io.mockk.every
 import io.mockk.impl.annotations.MockK
+import io.mockk.just
 import io.mockk.spyk
 import io.mockk.unmockkAll
 import kotlinx.coroutines.flow.flowOf
@@ -34,6 +37,7 @@ class SubmissionTestResultAvailableFragmentTest : BaseUITest() {
     @MockK lateinit var submissionRepository: SubmissionRepository
     @MockK lateinit var tekHistoryUpdaterFactory: TEKHistoryUpdater_Factory_Impl
     @MockK lateinit var autoSubmission: AutoSubmission
+    @MockK lateinit var appShortcutsHelper: AppShortcutsHelper
 
     @Rule
     @JvmField
@@ -48,6 +52,7 @@ class SubmissionTestResultAvailableFragmentTest : BaseUITest() {
 
         every { submissionRepository.deviceUIStateFlow } returns flowOf()
         every { submissionRepository.testResultReceivedDateFlow } returns flowOf()
+        every { appShortcutsHelper.removeAppShortcut() } just Runs
 
         viewModel = spyk(
             SubmissionTestResultAvailableViewModel(
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentModule.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentModule.kt
new file mode 100644
index 000000000..16dacec6e
--- /dev/null
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentModule.kt
@@ -0,0 +1,18 @@
+package de.rki.coronawarnapp.test.deltaonboarding.ui
+
+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 DeltaOnboardingFragmentModule {
+    @Binds
+    @IntoMap
+    @CWAViewModelKey(DeltaOnboardingFragmentViewModel::class)
+    abstract fun testTaskControllerFragment(
+        factory: DeltaOnboardingFragmentViewModel.Factory
+    ): CWAViewModelFactory<out CWAViewModel>
+}
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentViewModel.kt
new file mode 100644
index 000000000..6ca53f714
--- /dev/null
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentViewModel.kt
@@ -0,0 +1,53 @@
+package de.rki.coronawarnapp.test.deltaonboarding.ui
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.asLiveData
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import de.rki.coronawarnapp.contactdiary.ui.ContactDiarySettings
+import de.rki.coronawarnapp.environment.BuildConfigWrap
+import de.rki.coronawarnapp.main.CWASettings
+import de.rki.coronawarnapp.storage.LocalData
+import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
+import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
+
+class DeltaOnboardingFragmentViewModel @AssistedInject constructor(
+    private val settings: CWASettings,
+    private val contactDiarySettings: ContactDiarySettings,
+    dispatcherProvider: DispatcherProvider
+) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
+
+    val changelogVersion: LiveData<Long> =
+        settings.lastChangelogVersion.flow.asLiveData(context = dispatcherProvider.Default)
+
+    fun updateChangelogVersion(value: Long) {
+        settings.lastChangelogVersion.update { value }
+    }
+
+    fun resetChangelogVersion() {
+        settings.lastChangelogVersion.update { BuildConfigWrap.VERSION_CODE }
+    }
+
+    fun clearChangelogVersion() {
+        settings.lastChangelogVersion.update { 1 }
+    }
+
+    fun isContactJournalOnboardingDone() = contactDiarySettings.isOnboardingDone
+
+    fun setContactJournalOnboardingDone(value: Boolean) {
+        contactDiarySettings.onboardingStatus = if (value)
+            ContactDiarySettings.OnboardingStatus.RISK_STATUS_1_12
+        else
+            ContactDiarySettings.OnboardingStatus.NOT_ONBOARDED
+    }
+
+    fun isDeltaOnboardingDone() = LocalData.isInteroperabilityShownAtLeastOnce
+
+    fun setDeltaOboardinDone(value: Boolean) {
+        LocalData.isInteroperabilityShownAtLeastOnce = value
+    }
+
+    @AssistedFactory
+    interface Factory : SimpleCWAViewModelFactory<DeltaOnboardingFragmentViewModel>
+}
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaonboardingFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaonboardingFragment.kt
new file mode 100644
index 000000000..1179e8e07
--- /dev/null
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaonboardingFragment.kt
@@ -0,0 +1,62 @@
+package de.rki.coronawarnapp.test.deltaonboarding.ui
+
+import android.annotation.SuppressLint
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.Fragment
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.databinding.FragmentTestDeltaonboardingBinding
+import de.rki.coronawarnapp.test.menu.ui.TestMenuItem
+import de.rki.coronawarnapp.util.di.AutoInject
+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
+
+@SuppressLint("SetTextI18n")
+class DeltaonboardingFragment : Fragment(R.layout.fragment_test_deltaonboarding), AutoInject {
+
+    @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
+    private val viewModel: DeltaOnboardingFragmentViewModel by cwaViewModels { viewModelFactory }
+
+    private val binding: FragmentTestDeltaonboardingBinding by viewBindingLazy()
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        binding.switchContactJournalOnboarding.isChecked = viewModel.isContactJournalOnboardingDone()
+        binding.switchDeltaOnboarding.isChecked = viewModel.isDeltaOnboardingDone()
+        viewModel.changelogVersion.observe(viewLifecycleOwner) {
+            binding.lastChangelogEdittext.setText(it.toString())
+        }
+
+        binding.buttonSet.setOnClickListener {
+            val value = binding.lastChangelogEdittext.text.toString().toLong()
+            viewModel.updateChangelogVersion(value)
+        }
+
+        binding.buttonClear.setOnClickListener {
+            viewModel.clearChangelogVersion()
+        }
+
+        binding.buttonReset.setOnClickListener {
+            viewModel.resetChangelogVersion()
+        }
+
+        binding.switchContactJournalOnboarding.setOnCheckedChangeListener { _, value ->
+            viewModel.setContactJournalOnboardingDone(value)
+        }
+
+        binding.switchDeltaOnboarding.setOnCheckedChangeListener { _, value ->
+            viewModel.setDeltaOboardinDone(value)
+        }
+    }
+
+    companion object {
+        val MENU_ITEM = TestMenuItem(
+            title = "Onboarding options",
+            description = "Set delta onboarding or new release screen",
+            targetId = R.id.test_deltaonboarding_fragment
+        )
+    }
+}
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt
index 45d99a414..6d09d4e6b 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt
@@ -9,6 +9,7 @@ import de.rki.coronawarnapp.test.contactdiary.ui.ContactDiaryTestFragment
 import de.rki.coronawarnapp.test.crash.ui.SettingsCrashReportFragment
 import de.rki.coronawarnapp.test.datadonation.ui.DataDonationTestFragment
 import de.rki.coronawarnapp.test.debugoptions.ui.DebugOptionsFragment
+import de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaonboardingFragment
 import de.rki.coronawarnapp.test.keydownload.ui.KeyDownloadTestFragment
 import de.rki.coronawarnapp.test.playground.ui.PlaygroundFragment
 import de.rki.coronawarnapp.test.risklevel.ui.TestRiskLevelCalculationFragment
@@ -32,7 +33,8 @@ class TestMenuFragmentViewModel @AssistedInject constructor() : CWAViewModel() {
             MiscInfoFragment.MENU_ITEM,
             ContactDiaryTestFragment.MENU_ITEM,
             PlaygroundFragment.MENU_ITEM,
-            DataDonationTestFragment.MENU_ITEM
+            DataDonationTestFragment.MENU_ITEM,
+            DeltaonboardingFragment.MENU_ITEM
         ).let { MutableLiveData(it) }
     }
     val showTestScreenEvent = SingleLiveEvent<TestMenuItem>()
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt
index c0511503a..da9c43ddb 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt
@@ -12,6 +12,8 @@ import de.rki.coronawarnapp.test.datadonation.ui.DataDonationTestFragment
 import de.rki.coronawarnapp.test.datadonation.ui.DataDonationTestFragmentModule
 import de.rki.coronawarnapp.test.debugoptions.ui.DebugOptionsFragment
 import de.rki.coronawarnapp.test.debugoptions.ui.DebugOptionsFragmentModule
+import de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaOnboardingFragmentModule
+import de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaonboardingFragment
 import de.rki.coronawarnapp.test.keydownload.ui.KeyDownloadTestFragment
 import de.rki.coronawarnapp.test.keydownload.ui.KeyDownloadTestFragmentModule
 import de.rki.coronawarnapp.test.menu.ui.TestMenuFragment
@@ -60,4 +62,7 @@ abstract class MainActivityTestModule {
 
     @ContributesAndroidInjector(modules = [DataDonationTestFragmentModule::class])
     abstract fun dataDonation(): DataDonationTestFragment
+
+    @ContributesAndroidInjector(modules = [DeltaOnboardingFragmentModule::class])
+    abstract fun deltaOnboarding(): DeltaonboardingFragment
 }
diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_deltaonboarding.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_deltaonboarding.xml
new file mode 100644
index 000000000..374995566
--- /dev/null
+++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_deltaonboarding.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.core.widget.NestedScrollView 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:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fillViewport="true"
+    tools:ignore="HardcodedText">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/spacing_tiny"
+        android:orientation="vertical">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/debug_container"
+            style="@style/Card"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/spacing_tiny">
+
+            <TextView
+                android:id="@+id/delta_onboarding_title"
+                style="@style/headline6"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="New release info"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <TextView
+                android:id="@+id/explanation_label"
+                style="@style/body2"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_small"
+                android:text="By changing the last changelog version you may invoke the 'New release' screen in the home screen."
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/delta_onboarding_title" />
+
+            <com.google.android.material.textfield.TextInputLayout
+                android:id="@+id/last_changelog_layout"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_small"
+                android:hint="Last chnagelog version"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/explanation_label">
+
+                <EditText
+                    android:id="@+id/last_changelog_edittext"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:inputType="number" />
+
+            </com.google.android.material.textfield.TextInputLayout>
+
+            <Button
+                android:id="@+id/button_set"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_tiny"
+                android:text="Update"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/last_changelog_layout" />
+
+            <Button
+                android:id="@+id/button_clear"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_tiny"
+                android:text="Clear"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/button_set" />
+
+            <Button
+                android:id="@+id/button_reset"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_tiny"
+                android:text="Reset"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/button_clear" />
+
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/debug_container2"
+            style="@style/Card"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/spacing_tiny">
+
+            <TextView
+                android:id="@+id/contact_journal_onboarding_title"
+                style="@style/headline6"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Contact Journal onboarding"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <com.google.android.material.switchmaterial.SwitchMaterial
+                android:id="@+id/switch_contact_journal_onboarding"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_small"
+                android:text="Onboarding finished"
+                app:layout_constraintTop_toBottomOf="@id/contact_journal_onboarding_title" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/debug_container3"
+            style="@style/Card"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/spacing_tiny">
+
+            <TextView
+                android:id="@+id/cdelta_onboarding_title"
+                style="@style/headline6"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Delta onboarding"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <com.google.android.material.switchmaterial.SwitchMaterial
+                android:id="@+id/switch_delta_onboarding"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_small"
+                android:text="Onboarding finished"
+                app:layout_constraintTop_toBottomOf="@id/cdelta_onboarding_title" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+    </LinearLayout>
+</androidx.core.widget.NestedScrollView>
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 efdf34765..3fd5c94a3 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
@@ -43,6 +43,9 @@
         <action
             android:id="@+id/action_test_menu_fragment_to_dataDonationFragment"
             app:destination="@id/test_datadonation_fragment" />
+        <action
+            android:id="@+id/action_test_menu_fragment_to_deltaonboardingFragment"
+            app:destination="@id/test_deltaonboarding_fragment" />
     </fragment>
 
     <fragment
@@ -116,4 +119,10 @@
         tools:layout="@layout/fragment_test_datadonation"
         android:name="de.rki.coronawarnapp.test.datadonation.ui.DataDonationTestFragment"
         android:label="DataDonationFragment" />
+    <fragment
+        android:id="@+id/test_deltaonboarding_fragment"
+        android:name="de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaonboardingFragment"
+        android:label="DeltaonboardingFragment"
+        tools:layout="@layout/fragment_test_deltaonboarding" />
+
 </navigation>
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt
index e01721dc3..3155f696f 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt
@@ -3,7 +3,9 @@ package de.rki.coronawarnapp.contactdiary.ui.onboarding
 import android.os.Bundle
 import android.view.View
 import android.view.accessibility.AccessibilityEvent
+import androidx.core.net.toUri
 import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.findNavController
 import androidx.navigation.fragment.navArgs
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.contactdiary.ui.ContactDiarySettings
@@ -15,6 +17,7 @@ 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 org.joda.time.LocalDate
 import javax.inject.Inject
 
 class ContactDiaryOnboardingFragment : Fragment(R.layout.contact_diary_onboarding_fragment), AutoInject {
@@ -31,7 +34,6 @@ class ContactDiaryOnboardingFragment : Fragment(R.layout.contact_diary_onboardin
         super.onViewCreated(view, savedInstanceState)
         binding.apply {
             contactDiaryOnboardingNextButton.setOnClickListener {
-
                 vm.onNextButtonClick()
             }
             if (!args.showBottomNav) {
@@ -63,10 +65,17 @@ class ContactDiaryOnboardingFragment : Fragment(R.layout.contact_diary_onboardin
 
                 ContactDiaryOnboardingNavigationEvents.NavigateToOverviewFragment -> {
                     onboardingComplete()
-                    doNavigate(
-                        ContactDiaryOnboardingFragmentDirections
-                            .actionContactDiaryOnboardingFragmentToContactDiaryOverviewFragment()
-                    )
+                    if (arguments?.containsKey(OPEN_CURRENT_DAY) == true) {
+                        findNavController().apply {
+                            popBackStack(R.id.contactDiaryOnboardingFragment, true)
+                            navigate("coronawarnapp://contact-journal/day/${LocalDate()}".toUri())
+                        }
+                    } else {
+                        doNavigate(
+                            ContactDiaryOnboardingFragmentDirections
+                                .actionContactDiaryOnboardingFragmentToContactDiaryOverviewFragment()
+                        )
+                    }
                 }
             }
         }
@@ -80,4 +89,8 @@ class ContactDiaryOnboardingFragment : Fragment(R.layout.contact_diary_onboardin
         super.onResume()
         binding.contentContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT)
     }
+
+    companion object {
+        private const val OPEN_CURRENT_DAY = "goToDay"
+    }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivity.kt
index 8740ce471..90c3fe4bc 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivity.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivity.kt
@@ -9,6 +9,7 @@ import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.ui.main.MainActivity
 import de.rki.coronawarnapp.ui.onboarding.OnboardingActivity
 import de.rki.coronawarnapp.util.di.AppInjector
+import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
 import de.rki.coronawarnapp.util.viewmodel.cwaViewModels
 import javax.inject.Inject
@@ -16,6 +17,7 @@ import javax.inject.Inject
 class LauncherActivity : AppCompatActivity() {
 
     @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
+
     private val vm: LauncherActivityViewModel by cwaViewModels(
         ownerProducer = { viewModelStore },
         factoryProducer = { viewModelFactory }
@@ -28,12 +30,12 @@ class LauncherActivity : AppCompatActivity() {
         vm.events.observe(this) {
             when (it) {
                 LauncherEvent.GoToOnboarding -> {
-                    OnboardingActivity.start(this)
+                    OnboardingActivity.start(this, AppShortcutsHelper.getShortcutType(intent))
                     this.overridePendingTransition(0, 0)
                     finish()
                 }
                 LauncherEvent.GoToMainActivity -> {
-                    MainActivity.start(this)
+                    MainActivity.start(this, AppShortcutsHelper.getShortcutType(intent))
                     this.overridePendingTransition(0, 0)
                     finish()
                 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModel.kt
index d42ce56f0..acd7d63e6 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModel.kt
@@ -2,6 +2,8 @@ package de.rki.coronawarnapp.ui.launcher
 
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import de.rki.coronawarnapp.environment.BuildConfigWrap
+import de.rki.coronawarnapp.main.CWASettings
 import de.rki.coronawarnapp.storage.LocalData
 import de.rki.coronawarnapp.update.UpdateChecker
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
@@ -11,7 +13,8 @@ import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
 
 class LauncherActivityViewModel @AssistedInject constructor(
     private val updateChecker: UpdateChecker,
-    dispatcherProvider: DispatcherProvider
+    dispatcherProvider: DispatcherProvider,
+    private val cwaSettings: CWASettings,
 ) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
 
     val events = SingleLiveEvent<LauncherEvent>()
@@ -21,12 +24,16 @@ class LauncherActivityViewModel @AssistedInject constructor(
             val updateResult = updateChecker.checkForUpdate()
             when {
                 updateResult.isUpdateNeeded -> LauncherEvent.ShowUpdateDialog(updateResult.updateIntent?.invoke()!!)
-                LocalData.isOnboarded() -> LauncherEvent.GoToMainActivity
-                else -> LauncherEvent.GoToOnboarding
+                isJustInstalledOrUpdated() -> LauncherEvent.GoToOnboarding
+                else -> LauncherEvent.GoToMainActivity
             }.let { events.postValue(it) }
         }
     }
 
+    private fun isJustInstalledOrUpdated() =
+        !LocalData.isOnboarded() || !LocalData.isInteroperabilityShownAtLeastOnce ||
+            cwaSettings.lastChangelogVersion.value < BuildConfigWrap.VERSION_CODE
+
     @AssistedFactory
     interface Factory : SimpleCWAViewModelFactory<LauncherActivityViewModel>
 }
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 a0c2547e9..f6984c21e 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
@@ -7,21 +7,25 @@ import android.os.Bundle
 import android.provider.Settings
 import android.widget.Toast
 import androidx.appcompat.app.AppCompatActivity
+import androidx.core.net.toUri
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.FragmentManager
 import androidx.navigation.NavController
 import androidx.navigation.NavGraph
+import com.google.android.material.bottomnavigation.BottomNavigationView
 import dagger.android.AndroidInjector
 import dagger.android.DispatchingAndroidInjector
 import dagger.android.HasAndroidInjector
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.contactdiary.retention.ContactDiaryWorkScheduler
+import de.rki.coronawarnapp.contactdiary.ui.overview.ContactDiaryOverviewFragmentDirections
 import de.rki.coronawarnapp.databinding.ActivityMainBinding
 import de.rki.coronawarnapp.datadonation.analytics.worker.DataDonationAnalyticsScheduler
 import de.rki.coronawarnapp.deadman.DeadmanNotificationScheduler
 import de.rki.coronawarnapp.storage.LocalData
 import de.rki.coronawarnapp.ui.base.startActivitySafely
 import de.rki.coronawarnapp.ui.setupWithNavController2
+import de.rki.coronawarnapp.util.AppShortcuts
 import de.rki.coronawarnapp.util.CWADebug
 import de.rki.coronawarnapp.util.ConnectivityHelper
 import de.rki.coronawarnapp.util.DialogHelper
@@ -31,6 +35,7 @@ import de.rki.coronawarnapp.util.ui.findNavController
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
 import de.rki.coronawarnapp.util.viewmodel.cwaViewModels
 import de.rki.coronawarnapp.worker.BackgroundWorkScheduler
+import org.joda.time.LocalDate
 import javax.inject.Inject
 
 /**
@@ -42,8 +47,24 @@ import javax.inject.Inject
  */
 class MainActivity : AppCompatActivity(), HasAndroidInjector {
     companion object {
-        fun start(context: Context) {
-            context.startActivity(Intent(context, MainActivity::class.java))
+        private const val EXTRA_DATA = "shortcut"
+
+        fun start(context: Context, shortcut: AppShortcuts? = null) {
+            val intent = Intent(context, MainActivity::class.java).apply {
+                if (shortcut != null) {
+                    putExtra(EXTRA_DATA, shortcut.toString())
+                    flags = flags or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
+                }
+            }
+            context.startActivity(intent)
+        }
+
+        private fun getShortcutFromIntent(intent: Intent): AppShortcuts? {
+            val extra = intent.getStringExtra(EXTRA_DATA)
+            if (extra != null) {
+                return AppShortcuts.valueOf(extra)
+            }
+            return null
         }
     }
 
@@ -91,6 +112,37 @@ class MainActivity : AppCompatActivity(), HasAndroidInjector {
         vm.isOnboardingDone.observe(this) { isOnboardingDone ->
             startNestedGraphDestination(navController, isOnboardingDone)
         }
+
+        if (savedInstanceState == null) {
+            processExtraParameters()
+        }
+    }
+
+    private fun processExtraParameters() {
+        when (getShortcutFromIntent(intent)) {
+            AppShortcuts.CONTACT_DIARY -> {
+                goToContactJournal()
+            }
+        }
+    }
+
+    private fun goToContactJournal() {
+        val navController = supportFragmentManager.findNavController(R.id.nav_host_fragment)
+        findViewById<BottomNavigationView>(R.id.main_bottom_navigation).selectedItemId =
+            R.id.contact_diary_nav_graph
+        val nestedGraph = navController.graph.findNode(R.id.contact_diary_nav_graph) as NavGraph
+
+        if (vm.isOnboardingDone.value == true) {
+            nestedGraph.startDestination = R.id.contactDiaryOverviewFragment
+            navController.navigate(
+                ContactDiaryOverviewFragmentDirections.actionContactDiaryOverviewFragmentToContactDiaryDayFragment(
+                    selectedDay = LocalDate().toString()
+                )
+            )
+        } else {
+            nestedGraph.startDestination = R.id.contactDiaryOnboardingFragment
+            navController.navigate("coronawarnapp://contact-journal/oboarding/?goToDay=true".toUri())
+        }
     }
 
     private fun startNestedGraphDestination(navController: NavController, isOnboardingDone: Boolean) {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityActions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityActions.kt
new file mode 100644
index 000000000..f836e972d
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityActions.kt
@@ -0,0 +1,5 @@
+package de.rki.coronawarnapp.ui.main
+
+enum class MainActivityActions {
+    ADD_DIARY_ENTRY
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt
index f4c94877b..7db7bcbe8 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt
@@ -1,5 +1,7 @@
 package de.rki.coronawarnapp.ui.main
 
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import de.rki.coronawarnapp.contactdiary.ui.ContactDiarySettings
@@ -27,7 +29,8 @@ class MainActivityViewModel @AssistedInject constructor(
 
     val showBackgroundJobDisabledNotification = SingleLiveEvent<Unit>()
     val showEnergyOptimizedEnabledForBackground = SingleLiveEvent<Unit>()
-    val isOnboardingDone = SingleLiveEvent<Boolean>()
+    private val mutableIsOnboardingDone = MutableLiveData<Boolean>()
+    val isOnboardingDone: LiveData<Boolean> = mutableIsOnboardingDone
 
     init {
         if (CWADebug.isDeviceForTestersBuild) {
@@ -64,7 +67,7 @@ class MainActivityViewModel @AssistedInject constructor(
     }
 
     fun onBottomNavSelected() {
-        isOnboardingDone.value = contactDiarySettings.isOnboardingDone
+        mutableIsOnboardingDone.value = contactDiarySettings.isOnboardingDone
     }
 
     private suspend fun checkForEnergyOptimizedEnabled() {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt
index 0689d0144..cf4fa1501 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt
@@ -80,11 +80,6 @@ class HomeFragment : Fragment(R.layout.home_fragment_layout), AutoInject {
 
         vm.popupEvents.observe2(this) { event ->
             when (event) {
-                HomeFragmentEvents.ShowInteropDeltaOnboarding -> {
-                    doNavigate(
-                        HomeFragmentDirections.actionMainFragmentToOnboardingDeltaInteroperabilityFragment()
-                    )
-                }
                 is HomeFragmentEvents.ShowTracingExplanation -> {
                     tracingExplanationDialog.show(event.activeTracingDaysInRetentionPeriod) {
                         vm.tracingExplanationWasShown()
@@ -97,11 +92,6 @@ class HomeFragment : Fragment(R.layout.home_fragment_layout), AutoInject {
                     )
                 }
                 HomeFragmentEvents.ShowDeleteTestDialog -> showRemoveTestDialog()
-
-                HomeFragmentEvents.ShowNewReleaseFragment -> doNavigate(
-                    HomeFragmentDirections.actionMainFragmentToNewReleaseInfoFragment(false)
-                )
-
                 HomeFragmentEvents.GoToStatisticsExplanation -> doNavigate(
                     HomeFragmentDirections.actionMainFragmentToStatisticsExplanationFragment()
                 )
@@ -111,7 +101,7 @@ class HomeFragment : Fragment(R.layout.home_fragment_layout), AutoInject {
             }
         }
 
-        vm.showPopUpsOrNavigate()
+        vm.showPopUps()
 
         vm.showLoweredRiskLevelDialog.observe2(this) {
             if (it) showRiskLevelLoweredDialog()
@@ -127,6 +117,7 @@ class HomeFragment : Fragment(R.layout.home_fragment_layout), AutoInject {
     override fun onResume() {
         super.onResume()
         vm.refreshRequiredData()
+        vm.restoreAppShortcuts()
         binding.container.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT)
     }
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt
index 422dd904a..595e0aa4f 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt
@@ -1,7 +1,6 @@
 package de.rki.coronawarnapp.ui.main.home
 
 sealed class HomeFragmentEvents {
-    object ShowInteropDeltaOnboarding : HomeFragmentEvents()
 
     data class ShowTracingExplanation(
         val activeTracingDaysInRetentionPeriod: Long
@@ -13,7 +12,5 @@ sealed class HomeFragmentEvents {
 
     object ShowReactivateRiskCheckDialog : HomeFragmentEvents()
 
-    object ShowNewReleaseFragment : HomeFragmentEvents()
-
     object GoToStatisticsExplanation : HomeFragmentEvents()
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt
index bb3950f15..86309e8ec 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt
@@ -7,7 +7,6 @@ import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import de.rki.coronawarnapp.appconfig.AppConfigProvider
 import de.rki.coronawarnapp.deadman.DeadmanNotificationScheduler
-import de.rki.coronawarnapp.environment.BuildConfigWrap
 import de.rki.coronawarnapp.main.CWASettings
 import de.rki.coronawarnapp.notification.ShareTestResultNotificationService
 import de.rki.coronawarnapp.risk.TimeVariables
@@ -50,7 +49,6 @@ import de.rki.coronawarnapp.tracing.ui.homecards.TracingProgressCard
 import de.rki.coronawarnapp.tracing.ui.statusbar.TracingHeaderState
 import de.rki.coronawarnapp.tracing.ui.statusbar.toHeaderState
 import de.rki.coronawarnapp.ui.main.home.HomeFragmentEvents.ShowErrorResetDialog
-import de.rki.coronawarnapp.ui.main.home.HomeFragmentEvents.ShowInteropDeltaOnboarding
 import de.rki.coronawarnapp.ui.main.home.HomeFragmentEvents.ShowTracingExplanation
 import de.rki.coronawarnapp.ui.main.home.items.FAQCard
 import de.rki.coronawarnapp.ui.main.home.items.HomeItem
@@ -59,6 +57,7 @@ import de.rki.coronawarnapp.util.DeviceUIState
 import de.rki.coronawarnapp.util.NetworkRequestWrapper.Companion.withSuccess
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
 import de.rki.coronawarnapp.util.security.EncryptionErrorResetTool
+import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper
 import de.rki.coronawarnapp.util.ui.SingleLiveEvent
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
 import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
@@ -81,7 +80,8 @@ class HomeFragmentViewModel @AssistedInject constructor(
     private val cwaSettings: CWASettings,
     appConfigProvider: AppConfigProvider,
     statisticsProvider: StatisticsProvider,
-    private val deadmanNotificationScheduler: DeadmanNotificationScheduler
+    private val deadmanNotificationScheduler: DeadmanNotificationScheduler,
+    private val appShortcutsHelper: AppShortcutsHelper
 ) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
 
     private val tracingStateProvider by lazy { tracingStateProviderFactory.create(isDetailsMode = false) }
@@ -95,29 +95,19 @@ class HomeFragmentViewModel @AssistedInject constructor(
 
     val popupEvents = SingleLiveEvent<HomeFragmentEvents>()
 
-    fun showPopUpsOrNavigate() {
-        when {
-            !LocalData.isInteroperabilityShownAtLeastOnce -> {
-                popupEvents.postValue(ShowInteropDeltaOnboarding)
-            }
-            cwaSettings.lastChangelogVersion.value < BuildConfigWrap.VERSION_CODE -> {
-                popupEvents.postValue(HomeFragmentEvents.ShowNewReleaseFragment)
+    fun showPopUps() {
+        launch {
+            if (!LocalData.tracingExplanationDialogWasShown()) {
+                popupEvents.postValue(
+                    ShowTracingExplanation(
+                        TimeVariables.getActiveTracingDaysInRetentionPeriod()
+                    )
+                )
             }
-            else -> {
-                launch {
-                    if (!LocalData.tracingExplanationDialogWasShown()) {
-                        popupEvents.postValue(
-                            ShowTracingExplanation(
-                                TimeVariables.getActiveTracingDaysInRetentionPeriod()
-                            )
-                        )
-                    }
-                }
-                launch {
-                    if (errorResetTool.isResetNoticeToBeShown) {
-                        popupEvents.postValue(ShowErrorResetDialog)
-                    }
-                }
+        }
+        launch {
+            if (errorResetTool.isResetNoticeToBeShown) {
+                popupEvents.postValue(ShowErrorResetDialog)
             }
         }
     }
@@ -307,6 +297,12 @@ class HomeFragmentViewModel @AssistedInject constructor(
         }
     }
 
+    fun restoreAppShortcuts() {
+        launch {
+            appShortcutsHelper.restoreAppShortcut()
+        }
+    }
+
     fun tracingExplanationWasShown() {
         LocalData.tracingExplanationDialogWasShown(true)
     }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivity.kt
index a6b5218db..fe8a9252f 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivity.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivity.kt
@@ -15,6 +15,7 @@ import de.rki.coronawarnapp.environment.BuildConfigWrap
 import de.rki.coronawarnapp.main.CWASettings
 import de.rki.coronawarnapp.storage.LocalData
 import de.rki.coronawarnapp.ui.main.MainActivity
+import de.rki.coronawarnapp.util.AppShortcuts
 import de.rki.coronawarnapp.util.di.AppInjector
 import javax.inject.Inject
 
@@ -26,11 +27,21 @@ import javax.inject.Inject
 class OnboardingActivity : AppCompatActivity(), LifecycleObserver, HasAndroidInjector {
     companion object {
         private val TAG: String? = OnboardingActivity::class.simpleName
+        private const val EXTRA_DATA = "shortcut"
 
-        fun start(context: Context) {
-            val intent = Intent(context, OnboardingActivity::class.java)
+        fun start(context: Context, shortcut: AppShortcuts? = null) {
+            val intent = Intent(context, OnboardingActivity::class.java).apply {
+                putExtra(EXTRA_DATA, shortcut?.toString())
+            }
             context.startActivity(intent)
         }
+
+        fun getShortcutFromIntent(intent: Intent?): AppShortcuts? {
+            intent?.getStringExtra(EXTRA_DATA)?.let {
+                return AppShortcuts.valueOf(it)
+            }
+            return null
+        }
     }
 
     @Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivityModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivityModule.kt
index 34bc9a8a6..3c52d5e7c 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivityModule.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivityModule.kt
@@ -4,6 +4,8 @@ import dagger.Module
 import dagger.android.ContributesAndroidInjector
 import de.rki.coronawarnapp.datadonation.analytics.ui.AnalyticsUIModule
 import de.rki.coronawarnapp.datadonation.analytics.ui.input.AnalyticsUserInputFragment
+import de.rki.coronawarnapp.release.NewReleaseInfoFragment
+import de.rki.coronawarnapp.release.NewReleaseInfoFragmentModule
 
 @Module
 internal abstract class OnboardingActivityModule {
@@ -16,14 +18,31 @@ internal abstract class OnboardingActivityModule {
 
     @ContributesAndroidInjector(modules = [OnboardingTracingModule::class])
     abstract fun onboardingScreen(): OnboardingTracingFragment
+
     @ContributesAndroidInjector(modules = [OnboardingPrivacyModule::class])
     abstract fun onboardingPrivacyFragment(): OnboardingPrivacyFragment
+
     @ContributesAndroidInjector(modules = [OnboardingTestModule::class])
     abstract fun onboardingTestFragment(): OnboardingTestFragment
+
     @ContributesAndroidInjector(modules = [OnboardingNotificationsModule::class])
     abstract fun onboardingNotificationsFragment(): OnboardingNotificationsFragment
+
     @ContributesAndroidInjector(modules = [OnboardingAnalyticsModule::class])
     abstract fun onboardingAnalyticsFragment(): OnboardingAnalyticsFragment
+
     @ContributesAndroidInjector(modules = [AnalyticsUIModule::class])
     abstract fun ppaUserInfoSelection(): AnalyticsUserInputFragment
+
+    @ContributesAndroidInjector(modules = [OnboardingLoadingModule::class])
+    abstract fun onboardingLoadingScreen(): OnboardingLoadingFragment
+
+    @ContributesAndroidInjector(modules = [NewReleaseInfoFragmentModule::class])
+    abstract fun newReleaseInfoFragment(): NewReleaseInfoFragment
+
+    @ContributesAndroidInjector(modules = [OnboardingDeltaInteroperabilityModule::class])
+    abstract fun onboardingDeltaInteroperabilityFragment(): OnboardingDeltaInteroperabilityFragment
+
+    @ContributesAndroidInjector(modules = [OnboardingDeltaAnalyticsModule::class])
+    abstract fun onboardingDeltaAnalyticsFragment(): OnboardingDeltaAnalyticsFragment
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragment.kt
index 595e10ca9..527103651 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragment.kt
@@ -8,10 +8,10 @@ import androidx.navigation.fragment.findNavController
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.databinding.FragmentOnboardingDeltaInteroperabilityBinding
 import de.rki.coronawarnapp.ui.doNavigate
-import de.rki.coronawarnapp.ui.main.MainActivity
 import de.rki.coronawarnapp.util.convertToHyperlink
 import de.rki.coronawarnapp.util.di.AutoInject
 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
@@ -53,7 +53,7 @@ class OnboardingDeltaInteroperabilityFragment :
 
         vm.navigateBack.observe2(this) {
             if (it) {
-                (requireActivity() as MainActivity).goBack()
+                popBackStack()
             }
         }
     }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingFragment.kt
new file mode 100644
index 000000000..7d7c6475f
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingFragment.kt
@@ -0,0 +1,53 @@
+package de.rki.coronawarnapp.ui.onboarding
+
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.Fragment
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.ui.main.MainActivity
+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.viewmodel.CWAViewModelFactoryProvider
+import de.rki.coronawarnapp.util.viewmodel.cwaViewModels
+import javax.inject.Inject
+
+class OnboardingLoadingFragment : Fragment(R.layout.onboaring_loading_layout), AutoInject {
+
+    @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
+    private val viewModel: OnboardingLoadingViewModel by cwaViewModels(
+        ownerProducer = { requireActivity().viewModelStore },
+        factoryProducer = { viewModelFactory }
+    )
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        viewModel.navigationEvents.observe2(this) { event ->
+            when (event) {
+                OnboardingFragmentEvents.ShowInteropDeltaOnboarding ->
+                    doNavigate(
+                        OnboardingLoadingFragmentDirections
+                            .actionLoadingFragmentToOnboardingDeltaInteroperabilityFragment()
+                    )
+                OnboardingFragmentEvents.ShowNewReleaseFragment ->
+                    doNavigate(
+                        OnboardingLoadingFragmentDirections
+                            .actionLoadingFragmentToNewReleaseInfoFragment()
+                    )
+                OnboardingFragmentEvents.ShowOnboarding ->
+                    doNavigate(
+                        OnboardingLoadingFragmentDirections
+                            .actionLoadingFragmentToOnboardingFragment()
+                    )
+                OnboardingFragmentEvents.OnboardingDone -> {
+                    MainActivity.start(requireContext(), OnboardingActivity.getShortcutFromIntent(activity?.intent))
+                    activity?.overridePendingTransition(0, 0)
+                    activity?.finish()
+                }
+            }
+        }
+
+        viewModel.navigate()
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingModule.kt
new file mode 100644
index 000000000..40bb81564
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingModule.kt
@@ -0,0 +1,22 @@
+package de.rki.coronawarnapp.ui.onboarding
+
+import dagger.Binds
+import dagger.Module
+import dagger.android.ContributesAndroidInjector
+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 OnboardingLoadingModule {
+    @Binds
+    @IntoMap
+    @CWAViewModelKey(OnboardingLoadingViewModel::class)
+    abstract fun onboardingLoadingVM(
+        factory: OnboardingLoadingViewModel.Factory
+    ): CWAViewModelFactory<out CWAViewModel>
+
+    @ContributesAndroidInjector
+    abstract fun onboardingLoadingFragment(): OnboardingLoadingFragment
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingViewModel.kt
new file mode 100644
index 000000000..57dbcfc19
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingViewModel.kt
@@ -0,0 +1,46 @@
+package de.rki.coronawarnapp.ui.onboarding
+
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import de.rki.coronawarnapp.environment.BuildConfigWrap
+import de.rki.coronawarnapp.main.CWASettings
+import de.rki.coronawarnapp.storage.LocalData
+import de.rki.coronawarnapp.util.ui.SingleLiveEvent
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
+import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
+
+class OnboardingLoadingViewModel @AssistedInject constructor(private val cwaSettings: CWASettings) : CWAViewModel() {
+
+    val navigationEvents = SingleLiveEvent<OnboardingFragmentEvents>()
+
+    fun navigate() {
+        when {
+            !LocalData.isOnboarded() -> {
+                navigationEvents.postValue(OnboardingFragmentEvents.ShowOnboarding)
+            }
+            !LocalData.isInteroperabilityShownAtLeastOnce -> {
+                navigationEvents.postValue(OnboardingFragmentEvents.ShowInteropDeltaOnboarding)
+            }
+            cwaSettings.lastChangelogVersion.value < BuildConfigWrap.VERSION_CODE -> {
+                navigationEvents.postValue(OnboardingFragmentEvents.ShowNewReleaseFragment)
+            }
+            else -> {
+                navigationEvents.postValue(OnboardingFragmentEvents.OnboardingDone)
+            }
+        }
+    }
+
+    @AssistedFactory
+    interface Factory : SimpleCWAViewModelFactory<OnboardingLoadingViewModel>
+}
+
+sealed class OnboardingFragmentEvents {
+
+    object ShowInteropDeltaOnboarding : OnboardingFragmentEvents()
+
+    object ShowNewReleaseFragment : OnboardingFragmentEvents()
+
+    object ShowOnboarding : OnboardingFragmentEvents()
+
+    object OnboardingDone : OnboardingFragmentEvents()
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableFragment.kt
index 6a265ce44..c498a7942 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableFragment.kt
@@ -12,6 +12,7 @@ import de.rki.coronawarnapp.tracing.ui.TracingConsentDialog
 import de.rki.coronawarnapp.ui.submission.SubmissionBlockingDialog
 import de.rki.coronawarnapp.util.DialogHelper
 import de.rki.coronawarnapp.util.di.AutoInject
+import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper
 import de.rki.coronawarnapp.util.ui.doNavigate
 import de.rki.coronawarnapp.util.ui.observe2
 import de.rki.coronawarnapp.util.ui.viewBindingLazy
@@ -26,6 +27,7 @@ import javax.inject.Inject
  */
 class SubmissionTestResultAvailableFragment : Fragment(R.layout.fragment_submission_test_result_available), AutoInject {
 
+    @Inject lateinit var appShortcutsHelper: AppShortcutsHelper
     @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
     private val vm: SubmissionTestResultAvailableViewModel by cwaViewModels { viewModelFactory }
     private val binding: FragmentSubmissionTestResultAvailableBinding by viewBindingLazy()
@@ -88,6 +90,7 @@ class SubmissionTestResultAvailableFragment : Fragment(R.layout.fragment_submiss
     override fun onResume() {
         super.onResume()
         binding.submissionTestResultAvailableContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT)
+        appShortcutsHelper.removeAppShortcut()
     }
 
     private fun showCloseDialog() {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/AppShortcuts.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/AppShortcuts.kt
new file mode 100644
index 000000000..b4b340aab
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/AppShortcuts.kt
@@ -0,0 +1,5 @@
+package de.rki.coronawarnapp.util
+
+enum class AppShortcuts {
+    CONTACT_DIARY
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/shortcuts/AppShortcutsHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/shortcuts/AppShortcutsHelper.kt
new file mode 100644
index 000000000..0aeb1345d
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/shortcuts/AppShortcutsHelper.kt
@@ -0,0 +1,54 @@
+package de.rki.coronawarnapp.util.shortcuts
+
+import android.content.Context
+import android.content.Intent
+import androidx.core.content.pm.ShortcutInfoCompat
+import androidx.core.content.pm.ShortcutManagerCompat
+import androidx.core.graphics.drawable.IconCompat
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.ui.launcher.LauncherActivity
+import de.rki.coronawarnapp.util.AppShortcuts
+import de.rki.coronawarnapp.util.di.AppContext
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class AppShortcutsHelper @Inject constructor(@AppContext private val context: Context) {
+
+    suspend fun restoreAppShortcut() = withContext(Dispatchers.IO) {
+        if (ShortcutManagerCompat.getDynamicShortcuts(context).size == 0) {
+            val shortcut = ShortcutInfoCompat.Builder(context, CONTACT_DIARY_SHORTCUT_ID)
+                .setShortLabel(context.getString(R.string.app_shortcut_contact_diary_title))
+                .setLongLabel(context.getString(R.string.app_shortcut_contact_diary_title))
+                .setIcon(IconCompat.createWithResource(context, R.drawable.ic_contact_diary_shortcut_icon))
+                .setIntent(createContactDiaryIntent())
+                .build()
+
+            ShortcutManagerCompat.addDynamicShortcuts(context, listOf(shortcut))
+        }
+    }
+
+    fun removeAppShortcut() {
+        ShortcutManagerCompat.removeDynamicShortcuts(context, listOf(CONTACT_DIARY_SHORTCUT_ID))
+    }
+
+    private fun createContactDiaryIntent() = Intent(context, LauncherActivity::class.java).apply {
+        action = Intent.ACTION_VIEW
+        putExtra(SHORTCUT_EXTRA_ID, AppShortcuts.CONTACT_DIARY.toString())
+    }
+
+    companion object {
+        private const val CONTACT_DIARY_SHORTCUT_ID = "contact_diary_id"
+        private const val SHORTCUT_EXTRA_ID = "shortcut_extra"
+
+        fun getShortcutType(intent: Intent): AppShortcuts? {
+            intent.getStringExtra(SHORTCUT_EXTRA_ID)?.let {
+                return AppShortcuts.valueOf(it)
+            }
+
+            return null
+        }
+    }
+}
diff --git a/Corona-Warn-App/src/main/res/drawable/ic_contact_diary_shortcut_icon.xml b/Corona-Warn-App/src/main/res/drawable/ic_contact_diary_shortcut_icon.xml
new file mode 100644
index 000000000..b032e9572
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/drawable/ic_contact_diary_shortcut_icon.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="34dp"
+    android:height="34dp"
+    android:viewportWidth="34"
+    android:viewportHeight="34">
+  <path
+      android:pathData="M17,17m-17,0a17,17 0,1 1,34 0a17,17 0,1 1,-34 0"
+      android:fillColor="#F5F5F5"/>
+  <path
+      android:pathData="M24.3816,24.6345V10.1056C24.3816,8.8422 23.476,8 22.2126,8H11.9787C10.9164,8 9.9932,9.0445 10,10.1056C10,14.7983 10,19.7317 10,24.4238C10,25.6872 10.8425,26.5294 12.1546,26.5294H23.8971C23.8971,26.5294 24.3182,26.5294 24.3182,26.1083C24.3182,25.6872 24.3182,25.266 24.3182,25.266C24.3182,25.266 24.3182,24.8496 23.8971,24.8449C23.476,24.8402 12.948,24.8449 12.948,24.8449C12.948,24.8449 11.6845,24.8449 11.6845,23.5816C11.6845,23.0036 11.6845,22.7393 11.6845,22.7393C11.6845,21.4759 12.948,21.4759 12.948,21.4759C11.8333,21.4759 21.1642,21.4759 23.4759,21.4759C23.4759,21.4759 23.0548,22.3182 23.0548,23.1604C23.0548,23.6429 23.4759,24.6345 23.4759,24.6345C23.8971,24.6345 24.3816,24.6345 24.3816,24.6345Z"
+      android:fillColor="#777677"/>
+</vector>
diff --git a/Corona-Warn-App/src/main/res/layout/onboaring_loading_layout.xml b/Corona-Warn-App/src/main/res/layout/onboaring_loading_layout.xml
new file mode 100644
index 000000000..ab53df91a
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/onboaring_loading_layout.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <ProgressBar
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:layout_width="32dp"
+        android:layout_height="32dp" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/navigation/contact_diary_nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/contact_diary_nav_graph.xml
index a11f6a236..97572c5b0 100644
--- a/Corona-Warn-App/src/main/res/navigation/contact_diary_nav_graph.xml
+++ b/Corona-Warn-App/src/main/res/navigation/contact_diary_nav_graph.xml
@@ -19,6 +19,7 @@
         <action
             android:id="@+id/action_contactDiaryDayFragment_to_contactDiaryLocationBottomSheetDialogFragment"
             app:destination="@id/contactDiaryLocationBottomSheetDialogFragment" />
+        <deepLink app:uri="coronawarnapp://contact-journal/day/{selectedDay}" />
     </fragment>
     <fragment
         android:id="@+id/contactDiaryPersonListFragment"
@@ -87,6 +88,9 @@
             android:name="showBottomNav"
             android:defaultValue="true"
             app:argType="boolean" />
+        <deepLink app:uri="coronawarnapp://contact-journal/oboarding/?goToDay={goToDay}"
+            app:popUpTo="@id/contact_diary_nav_graph"
+            app:popUpToInclusive="true" />
     </fragment>
     <fragment
         android:id="@+id/contactDiaryInformationPrivacyFragment"
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 5bfc4298e..b4fe423e3 100644
--- a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml
+++ b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml
@@ -49,18 +49,12 @@
         <action
             android:id="@+id/action_mainFragment_to_submissionDispatcher"
             app:destination="@id/submissionDispatcherFragment" />
-        <action
-            android:id="@+id/action_mainFragment_to_onboardingDeltaInteroperabilityFragment"
-            app:destination="@id/onboardingDeltaInteroperabilityFragment" />
         <action
             android:id="@+id/action_mainFragment_to_test_nav_graph"
             app:destination="@id/test_nav_graph" />
         <action
             android:id="@+id/action_mainFragment_to_submissionTestResultAvailableFragment"
             app:destination="@id/submissionTestResultAvailableFragment" />
-        <action
-            android:id="@+id/action_mainFragment_to_newReleaseInfoFragment"
-            app:destination="@id/newReleaseInfoFragment" />
         <action
             android:id="@+id/action_mainFragment_to_statisticsExplanationFragment"
             app:destination="@id/statisticsExplanationFragment" />
diff --git a/Corona-Warn-App/src/main/res/navigation/nav_graph_onboarding.xml b/Corona-Warn-App/src/main/res/navigation/nav_graph_onboarding.xml
index 0e2a1bc2b..3c128a61f 100644
--- a/Corona-Warn-App/src/main/res/navigation/nav_graph_onboarding.xml
+++ b/Corona-Warn-App/src/main/res/navigation/nav_graph_onboarding.xml
@@ -3,8 +3,23 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/nav_graph_onboarding"
-    app:startDestination="@id/onboardingFragment">
+    app:startDestination="@id/loadingFragment">
 
+    <fragment
+        android:id="@+id/loadingFragment"
+        android:name="de.rki.coronawarnapp.ui.onboarding.OnboardingLoadingFragment"
+        android:label="LoadingFragment"
+        tools:layout="@layout/onboaring_loading_layout">
+        <action
+            android:id="@+id/action_loadingFragment_to_newReleaseInfoFragment"
+            app:destination="@id/newReleaseInfoFragment" />
+        <action
+            android:id="@+id/action_loadingFragment_to_onboardingDeltaInteroperabilityFragment"
+            app:destination="@id/onboardingDeltaInteroperabilityFragment2" />
+        <action
+            android:id="@+id/action_loadingFragment_to_onboardingFragment"
+            app:destination="@id/onboardingFragment" />
+    </fragment>
     <fragment
         android:id="@+id/onboardingFragment"
         android:name="de.rki.coronawarnapp.ui.onboarding.OnboardingFragment"
@@ -76,4 +91,48 @@
         android:name="de.rki.coronawarnapp.datadonation.analytics.ui.PpaMoreInfoFragment"
         android:label="PpaMoreInfoFragment"
         tools:layout="@layout/fragment_ppa_more_info" />
+
+    <!-- New Release -->
+    <fragment
+        android:id="@+id/newReleaseInfoFragment"
+        android:name="de.rki.coronawarnapp.release.NewReleaseInfoFragment"
+        tools:layout="@layout/new_release_info_screen_fragment"
+        android:label="NewReleaseInfoFragment">
+        <argument
+            android:name="comesFromInfoScreen"
+            android:defaultValue="false"
+            app:argType="boolean" />
+        <action
+            android:id="@+id/action_newReleaseInfoFragment_to_onboardingDeltaAnalyticsFragment"
+            app:destination="@id/onboardingDeltaAnalyticsFragment"
+            app:popUpTo="@id/loadingFragment"
+            app:popUpToInclusive="false" />
+    </fragment>
+    <fragment
+        android:id="@+id/onboardingDeltaAnalyticsFragment"
+        android:name="de.rki.coronawarnapp.ui.onboarding.OnboardingDeltaAnalyticsFragment"
+        android:label="OnboardingDeltaAnalyticsFragment"
+        tools:layout="@layout/fragment_onboarding_delta_ppa">
+        <action
+            android:id="@+id/action_onboardingDeltaAnalyticsFragment_to_analyticsUserInputFragment"
+            app:destination="@id/analyticsUserInputFragment" />
+        <action
+            android:id="@+id/action_onboardingDeltaAnalyticsFragment_to_ppaMoreInfoFragment"
+            app:destination="@id/ppaMoreInfoFragment" />
+    </fragment>
+    <fragment
+        android:id="@+id/onboardingDeltaInteroperabilityFragment2"
+        android:name="de.rki.coronawarnapp.ui.onboarding.OnboardingDeltaInteroperabilityFragment"
+        android:label="OnboardingDeltaInteroperabilityFragment"
+        tools:layout="@layout/fragment_onboarding_delta_interoperability">
+        <action
+            android:id="@+id/action_onboardingDeltaInteroperabilityFragment_to_informationTermsFragment"
+            app:destination="@id/informationTermsFragment" />
+    </fragment>
+
+    <fragment
+        android:id="@+id/informationTermsFragment"
+        android:name="de.rki.coronawarnapp.ui.information.InformationTermsFragment"
+        android:label="@layout/fragment_information_terms"
+        tools:layout="@layout/fragment_information_terms" />
 </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 baa753516..21ad6bdfe 100644
--- a/Corona-Warn-App/src/main/res/values-de/strings.xml
+++ b/Corona-Warn-App/src/main/res/values-de/strings.xml
@@ -1755,6 +1755,9 @@
     <!-- XBUT: Abort button for test result positive no consent screen -->
     <string name="submission_test_result_positive_no_consent_button_abort">Abbrechen</string>
 
+    <!-- XHED: Title of contact diary app shortcut -->
+    <string name="app_shortcut_contact_diary_title">"Eintrag hinzufügen"</string>
+
     <!-- XTXT: Statistics information (Accessibilty) -->
     <string name="statistics_info_button">"Weitere Informationen"</string>
     <!-- XHED: Title for statistics reproduction card -->
@@ -1878,4 +1881,5 @@
     <string name="analytics_userinput_district_title">Ihr Kreis/Bezirk</string>
     <!-- XTXT: Analytics voluntary user input, district: UNSPECIFIED -->
     <string name="analytics_userinput_district_unspecified">keine Angabe</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 de1bd2008..1971d65ab 100644
--- a/Corona-Warn-App/src/main/res/values/strings.xml
+++ b/Corona-Warn-App/src/main/res/values/strings.xml
@@ -1770,6 +1770,9 @@
     <!-- XBUT: Abort button for test result positive no consent screen -->
     <string name="submission_test_result_positive_no_consent_button_abort">"Cancel"</string>
 
+    <!-- XHED: Title of contact diary app shortcut -->
+    <string name="app_shortcut_contact_diary_title">"Eintrag hinzufügen"</string>
+
     <!-- XTXT: Statistics information (Accessibilty) -->
     <string name="statistics_info_button">"Further information"</string>
     <!-- XHED: Title for statistics reproduction card -->
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt
index c7c651a71..0ffe699ce 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt
@@ -24,6 +24,7 @@ import de.rki.coronawarnapp.util.DeviceUIState.PAIRED_POSITIVE
 import de.rki.coronawarnapp.util.DeviceUIState.PAIRED_POSITIVE_TELETAN
 import de.rki.coronawarnapp.util.NetworkRequestWrapper
 import de.rki.coronawarnapp.util.security.EncryptionErrorResetTool
+import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper
 import io.kotest.matchers.shouldBe
 import io.mockk.MockKAnnotations
 import io.mockk.clearAllMocks
@@ -65,6 +66,7 @@ class HomeFragmentViewModelTest : BaseTest() {
     @MockK lateinit var appConfigProvider: AppConfigProvider
     @MockK lateinit var statisticsProvider: StatisticsProvider
     @MockK lateinit var deadmanNotificationScheduler: DeadmanNotificationScheduler
+    @MockK lateinit var appShortcutsHelper: AppShortcutsHelper
 
     @BeforeEach
     fun setup() {
@@ -100,7 +102,8 @@ class HomeFragmentViewModelTest : BaseTest() {
         cwaSettings = cwaSettings,
         appConfigProvider = appConfigProvider,
         statisticsProvider = statisticsProvider,
-        deadmanNotificationScheduler = deadmanNotificationScheduler
+        deadmanNotificationScheduler = deadmanNotificationScheduler,
+        appShortcutsHelper = appShortcutsHelper
     )
 
     @Test
@@ -196,16 +199,10 @@ class HomeFragmentViewModelTest : BaseTest() {
         every { errorResetTool.isResetNoticeToBeShown } returns false andThen true
 
         with(createInstance()) {
-            showPopUpsOrNavigate()
-            popupEvents.getOrAwaitValue() shouldBe HomeFragmentEvents.ShowInteropDeltaOnboarding
-
-            showPopUpsOrNavigate()
-            popupEvents.getOrAwaitValue() shouldBe HomeFragmentEvents.ShowNewReleaseFragment
-
-            showPopUpsOrNavigate()
+            showPopUps()
             popupEvents.getOrAwaitValue() shouldBe HomeFragmentEvents.ShowTracingExplanation(1)
 
-            showPopUpsOrNavigate()
+            showPopUps()
             popupEvents.getOrAwaitValue() shouldBe HomeFragmentEvents.ShowErrorResetDialog
         }
     }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModelTest.kt
index 63b120314..930986dbe 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModelTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModelTest.kt
@@ -1,5 +1,7 @@
 package de.rki.coronawarnapp.ui.launcher
 
+import de.rki.coronawarnapp.environment.BuildConfigWrap
+import de.rki.coronawarnapp.main.CWASettings
 import de.rki.coronawarnapp.storage.LocalData
 import de.rki.coronawarnapp.update.UpdateChecker
 import io.kotest.matchers.shouldBe
@@ -16,11 +18,13 @@ import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.extension.ExtendWith
 import testhelpers.TestDispatcherProvider
 import testhelpers.extensions.InstantExecutorExtension
+import testhelpers.preferences.mockFlowPreference
 
 @ExtendWith(InstantExecutorExtension::class)
 class LauncherActivityViewModelTest {
 
     @MockK lateinit var updateChecker: UpdateChecker
+    @MockK lateinit var cwaSettings: CWASettings
 
     @BeforeEach
     fun setupFreshViewModel() {
@@ -29,12 +33,16 @@ class LauncherActivityViewModelTest {
         mockkObject(LocalData)
         every { LocalData.isOnboarded() } returns false
 
+        mockkObject(BuildConfigWrap)
+        every { BuildConfigWrap.VERSION_CODE } returns 10L
+
         coEvery { updateChecker.checkForUpdate() } returns UpdateChecker.Result(isUpdateNeeded = false)
     }
 
     private fun createViewModel() = LauncherActivityViewModel(
         updateChecker = updateChecker,
-        dispatcherProvider = TestDispatcherProvider()
+        dispatcherProvider = TestDispatcherProvider(),
+        cwaSettings = cwaSettings
     )
 
     @Test
@@ -59,6 +67,8 @@ class LauncherActivityViewModelTest {
     @Test
     fun `onboarding finished`() {
         every { LocalData.isOnboarded() } returns true
+        every { LocalData.isInteroperabilityShownAtLeastOnce } returns true
+        every { cwaSettings.lastChangelogVersion } returns mockFlowPreference(10L)
 
         val vm = createViewModel()
 
-- 
GitLab