From fd69ed4bc94563ce46c532a518dbcd59b809f499 Mon Sep 17 00:00:00 2001
From: Lukas Lechner <lukas.lechner@sap.com>
Date: Mon, 22 Feb 2021 16:35:06 +0100
Subject: [PATCH] Skip consent screen on subsequent survey participations
 (EXPOSUREAPP-5278) (#2433)

* Skip consent screen on subsequent survey participates

* Import Surveys.ConsentResult to make code more readable

* Improve wording, change ConsentResult.NotNeeded to ConsentResult.AlreadyGiven
---
 .../ui/tracing/TracingDetailsFragmentTest.kt  |  7 +++--
 .../datadonation/survey/Surveys.kt            | 30 +++++++++++++++++++
 .../ui/details/TracingDetailsFragment.kt      |  5 ++++
 .../TracingDetailsFragmentViewModel.kt        | 21 +++++++++++--
 .../details/TracingDetailsNavigationEvents.kt |  3 +-
 5 files changed, 61 insertions(+), 5 deletions(-)

diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/tracing/TracingDetailsFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/tracing/TracingDetailsFragmentTest.kt
index cd1ae371d..b9da670a2 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/tracing/TracingDetailsFragmentTest.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/tracing/TracingDetailsFragmentTest.kt
@@ -4,6 +4,7 @@ import androidx.lifecycle.MutableLiveData
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import dagger.Module
 import dagger.android.ContributesAndroidInjector
+import de.rki.coronawarnapp.datadonation.survey.Surveys
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.storage.TracingRepository
 import de.rki.coronawarnapp.tracing.GeneralTracingStatus
@@ -24,12 +25,12 @@ import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import testhelpers.BaseUITest
-import testhelpers.takeScreenshot
 import testhelpers.Screenshot
 import testhelpers.SystemUIDemoModeRule
 import testhelpers.TestDispatcherProvider
 import testhelpers.launchFragment2
 import testhelpers.launchFragmentInContainer2
+import testhelpers.takeScreenshot
 import tools.fastlane.screengrab.locale.LocaleTestRule
 
 @RunWith(AndroidJUnit4::class)
@@ -41,6 +42,7 @@ class TracingDetailsFragmentTest : BaseUITest() {
     @MockK lateinit var tracingDetailsItemProvider: TracingDetailsItemProvider
     @MockK lateinit var tracingStateProviderFactory: TracingStateProvider.Factory
     @MockK lateinit var tracingRepository: TracingRepository
+    @MockK lateinit var surveys: Surveys
 
     private lateinit var viewModel: TracingDetailsFragmentViewModel
 
@@ -63,7 +65,8 @@ class TracingDetailsFragmentTest : BaseUITest() {
                 riskLevelStorage = riskLevelStorage,
                 tracingDetailsItemProvider = tracingDetailsItemProvider,
                 tracingStateProviderFactory = tracingStateProviderFactory,
-                tracingRepository = tracingRepository
+                tracingRepository = tracingRepository,
+                surveys = surveys
             )
         )
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/survey/Surveys.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/survey/Surveys.kt
index 6914f0d81..121025261 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/survey/Surveys.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/survey/Surveys.kt
@@ -37,6 +37,36 @@ class Surveys @Inject constructor(
             }
     }
 
+    suspend fun isConsentNeeded(type: Type): ConsentResult {
+
+        when (type) {
+            Type.HIGH_RISK_ENCOUNTER -> {
+
+                // If no OTP was ever authorized, we need a consent.
+                val authResult = oneTimePasswordRepo.otpAuthorizationResult ?: return ConsentResult.Needed
+
+                // If we already have an authorized OTP for this high-risk state
+                // we can skip the consent and directly show the url in the browser.
+                // We know that the otp belongs to the current high-risk state, because the authResult gets
+                // invalidated on high to low risk state transitions.
+                if (authResult.authorized && !authResult.invalidated) {
+                    val surveyLink = urlProvider.provideUrl(type, authResult.uuid)
+                    return ConsentResult.AlreadyGiven(surveyLink)
+                }
+
+                // Finally, we need a consent for stored OTPs where the authorization failed (authorized == false)
+                // or when the app shows a new high-risk card (and therefore authResult was previously invalidated when the
+                // risk changed from high to low)
+                return ConsentResult.Needed
+            }
+        }
+    }
+
+    sealed class ConsentResult {
+        object Needed : ConsentResult()
+        data class AlreadyGiven(val surveyLink: String) : ConsentResult()
+    }
+
     suspend fun requestDetails(type: Type): Survey {
         val config = appConfigProvider.getAppConfig().survey
         Timber.v("Requested survey: %s", config)
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragment.kt
index b21bf698e..7732c3938 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragment.kt
@@ -8,6 +8,7 @@ import androidx.recyclerview.widget.DefaultItemAnimator
 import androidx.recyclerview.widget.LinearLayoutManager
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.databinding.TracingDetailsFragmentLayoutBinding
+import de.rki.coronawarnapp.util.ExternalActionHelper
 import de.rki.coronawarnapp.util.di.AutoInject
 import de.rki.coronawarnapp.util.lists.diffutil.update
 import de.rki.coronawarnapp.util.ui.doNavigate
@@ -55,6 +56,10 @@ class TracingDetailsFragment : Fragment(R.layout.tracing_details_fragment_layout
                 is TracingDetailsNavigationEvents.NavigateToSurveyConsentFragment -> doNavigate(
                     TracingDetailsFragmentDirections.actionRiskDetailsFragmentToSurveyConsentFragment(it.type)
                 )
+                is TracingDetailsNavigationEvents.NavigateToSurveyUrlInBrowser -> ExternalActionHelper.openUrl(
+                    this,
+                    it.url
+                )
             }
         }
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragmentViewModel.kt
index df6725509..e7fa20879 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragmentViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragmentViewModel.kt
@@ -4,6 +4,9 @@ import androidx.lifecycle.LiveData
 import androidx.lifecycle.asLiveData
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import de.rki.coronawarnapp.datadonation.survey.Surveys
+import de.rki.coronawarnapp.datadonation.survey.Surveys.ConsentResult.AlreadyGiven
+import de.rki.coronawarnapp.datadonation.survey.Surveys.ConsentResult.Needed
 import de.rki.coronawarnapp.risk.RiskState
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.risk.tryLatestResultsWithDefaults
@@ -42,7 +45,8 @@ class TracingDetailsFragmentViewModel @AssistedInject constructor(
     riskLevelStorage: RiskLevelStorage,
     tracingDetailsItemProvider: TracingDetailsItemProvider,
     tracingStateProviderFactory: TracingStateProvider.Factory,
-    private val tracingRepository: TracingRepository
+    private val tracingRepository: TracingRepository,
+    private val surveys: Surveys
 ) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
 
     private val tracingStateProvider by lazy { tracingStateProviderFactory.create(isDetailsMode = true) }
@@ -108,7 +112,20 @@ class TracingDetailsFragmentViewModel @AssistedInject constructor(
     fun onItemClicked(item: DetailsItem) {
         when (item) {
             is UserSurveyBox.Item ->
-                routeToScreen.postValue(TracingDetailsNavigationEvents.NavigateToSurveyConsentFragment(item.type))
+                launch {
+                    when (val consentResult = surveys.isConsentNeeded(Surveys.Type.HIGH_RISK_ENCOUNTER)) {
+                        is Needed -> routeToScreen.postValue(
+                            TracingDetailsNavigationEvents.NavigateToSurveyConsentFragment(
+                                item.type
+                            )
+                        )
+                        is AlreadyGiven -> routeToScreen.postValue(
+                            TracingDetailsNavigationEvents.NavigateToSurveyUrlInBrowser(
+                                consentResult.surveyLink
+                            )
+                        )
+                    }
+                }
         }
     }
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsNavigationEvents.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsNavigationEvents.kt
index 221ebc91e..9ba870a63 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsNavigationEvents.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsNavigationEvents.kt
@@ -3,5 +3,6 @@ package de.rki.coronawarnapp.tracing.ui.details
 import de.rki.coronawarnapp.datadonation.survey.Surveys
 
 sealed class TracingDetailsNavigationEvents {
-    class NavigateToSurveyConsentFragment(val type: Surveys.Type) : TracingDetailsNavigationEvents()
+    data class NavigateToSurveyConsentFragment(val type: Surveys.Type) : TracingDetailsNavigationEvents()
+    data class NavigateToSurveyUrlInBrowser(val url: String) : TracingDetailsNavigationEvents()
 }
-- 
GitLab