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 cd1ae371d21040b73673b24272918215fea7f634..b9da670a20ca57ebbddf6ba6492d4687e788f5dc 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 6914f0d81106fbe62dfc4fd080e8bf7436185ffc..1210252617c853a799da456b87fa08a5c997cac3 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 b21bf698e80c4186fd840cca67e761700809b76f..7732c39381124be8d7f5fa910e53c814247d6d56 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 df6725509fbc286e8108d1f01abe82866a2596c0..e7fa208798eaa4d03d3ace92c00d141d499ae932 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 221ebc91e8e9ebd767706a140276e2647c3f1749..9ba870a63c02d6048d225a6d84114e3061e8e730 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() }