From df8d0057cfd18c38f1c8c3d00d15bb04a50e3cb2 Mon Sep 17 00:00:00 2001 From: BMItter <46747780+BMItter@users.noreply.github.com> Date: Thu, 19 Nov 2020 19:16:28 +0100 Subject: [PATCH] ENFv2 - Refactor testmenu to use exposure window (EXPOSUREAPP-3845) (#1675) * Show aggregated risk result in testmenu riskCalculationFragment * Use extention fun to build string for aggregated result * Show exposure window count and exposure windows as json + fixed a layoutbug with cut off * satisfy lint * Use real values * Connected ExposureResultStore * ENF v2 Calculation adjusted Testmenu Entry for v2 * Adjusted to ExposureResultStore changes * Added logs * Removed and adjusted some old stuff * Made backend parameters a little bit better readable * its better this way * Created additional risk calc info * Refactoring * sourcecheck clean --- .../ui/TestRiskLevelCalculationFragment.kt | 65 +----- ...iskLevelCalculationFragmentCWAViewModel.kt | 214 +++++++----------- .../fragment_test_risk_level_calculation.xml | 60 +---- .../storage/RiskLevelRepository.kt | 2 +- 4 files changed, 112 insertions(+), 229 deletions(-) diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragment.kt index 9c8a948bb..b0e40407c 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragment.kt @@ -1,18 +1,13 @@ package de.rki.coronawarnapp.test.risklevel.ui -import android.content.Intent import android.os.Bundle import android.view.View import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.navArgs -import com.google.zxing.integration.android.IntentIntegrator -import com.google.zxing.integration.android.IntentResult import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentTestRiskLevelCalculationBinding -import de.rki.coronawarnapp.server.protocols.AppleLegacyKeyExchange -import de.rki.coronawarnapp.sharing.ExposureSharingService import de.rki.coronawarnapp.test.menu.ui.TestMenuItem import de.rki.coronawarnapp.ui.viewmodel.SettingsViewModel import de.rki.coronawarnapp.util.di.AutoInject @@ -20,7 +15,6 @@ 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.cwaViewModelsAssisted -import timber.log.Timber import javax.inject.Inject @Suppress("LongMethod") @@ -55,7 +49,6 @@ class TestRiskLevelCalculationFragment : Fragment(R.layout.fragment_test_risk_le } binding.buttonRetrieveDiagnosisKeys.setOnClickListener { vm.retrieveDiagnosisKeys() } - binding.buttonProvideKeyViaQr.setOnClickListener { vm.scanLocalQRCodeAndProvide() } binding.buttonCalculateRiskLevel.setOnClickListener { vm.calculateRiskLevel() } binding.buttonClearDiagnosisKeyCache.setOnClickListener { vm.clearKeyCache() } @@ -67,66 +60,32 @@ class TestRiskLevelCalculationFragment : Fragment(R.layout.fragment_test_risk_le ).show() } - vm.riskScoreState.observe2(this) { state -> - binding.labelRiskScore.text = state.riskScoreMsg - binding.labelBackendParameters.text = state.backendParameters - binding.labelExposureSummary.text = state.exposureSummary - binding.labelFormula.text = state.formula - binding.labelExposureInfo.text = state.exposureInfo + vm.additionalRiskCalcInfo.observe2(this) { + binding.labelRiskAdditionalInfo.text = it } - vm.startENFObserver() - vm.apiKeysProvidedEvent.observe2(this) { event -> - Toast.makeText( - requireContext(), - "Provided ${event.keyCount} keys to Google API", - Toast.LENGTH_SHORT - ).show() + vm.aggregatedRiskResult.observe2(this) { + binding.labelAggregatedRiskResult.text = it } - vm.startLocalQRCodeScanEvent.observe2(this) { - IntentIntegrator.forSupportFragment(this) - .setOrientationLocked(false) - .setBeepEnabled(false) - .initiateScan() + vm.exposureWindowCountString.observe2(this) { + binding.labelExposureWindowCount.text = it } - } - - override fun onResume() { - super.onResume() - vm.calculateRiskLevel() - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - val result: IntentResult = - IntentIntegrator.parseActivityResult(requestCode, resultCode, data) - ?: return super.onActivityResult(requestCode, resultCode, data) - if (result.contents == null) { - Toast.makeText(requireContext(), "Cancelled", Toast.LENGTH_LONG).show() - return + vm.exposureWindows.observe2(this) { + binding.labelExposureWindows.text = it } - ExposureSharingService.getOthersKeys(result.contents) { key: AppleLegacyKeyExchange.Key? -> - Timber.i("Keys scanned: %s", key) - if (key == null) { - Toast.makeText( - requireContext(), "No Key data found in QR code", Toast.LENGTH_SHORT - ).show() - return@getOthersKeys Unit - } - - val text = binding.transmissionNumber.text.toString() - val number = if (!text.isBlank()) Integer.valueOf(text) else 5 - vm.provideDiagnosisKey(number, key) + vm.backendParameters.observe2(this) { + binding.labelBackendParameters.text = it } } companion object { val TAG: String = TestRiskLevelCalculationFragment::class.simpleName!! val MENU_ITEM = TestMenuItem( - title = "Risklevel Calculation", - description = "Risklevel calculation related test options.", + title = "ENF v2 Calculation", + description = "Window Mode related overview.", targetId = R.id.test_risklevel_calculation_fragment ) } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt index 67d68216e..24e49d875 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt @@ -1,24 +1,23 @@ package de.rki.coronawarnapp.test.risklevel.ui import android.content.Context -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.asLiveData +import com.google.gson.Gson import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import de.rki.coronawarnapp.appconfig.AppConfigProvider +import de.rki.coronawarnapp.appconfig.ConfigData import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysTask import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report -import de.rki.coronawarnapp.nearby.ENFClient import de.rki.coronawarnapp.risk.ExposureResult import de.rki.coronawarnapp.risk.ExposureResultStore import de.rki.coronawarnapp.risk.RiskLevel import de.rki.coronawarnapp.risk.RiskLevelTask -import de.rki.coronawarnapp.risk.RiskLevels import de.rki.coronawarnapp.risk.TimeVariables -import de.rki.coronawarnapp.server.protocols.AppleLegacyKeyExchange +import de.rki.coronawarnapp.risk.result.AggregatedRiskResult import de.rki.coronawarnapp.storage.AppDatabase import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.storage.RiskLevelRepository @@ -27,11 +26,11 @@ import de.rki.coronawarnapp.task.TaskController import de.rki.coronawarnapp.task.common.DefaultTaskRequest import de.rki.coronawarnapp.task.submitBlocking import de.rki.coronawarnapp.ui.tracing.card.TracingCardStateProvider -import de.rki.coronawarnapp.util.KeyFileHelper import de.rki.coronawarnapp.util.NetworkRequestWrapper.Companion.withSuccess import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.di.AppContext import de.rki.coronawarnapp.util.security.SecurityHelper +import de.rki.coronawarnapp.util.serialization.BaseGson import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory @@ -39,9 +38,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.sample import kotlinx.coroutines.withContext +import org.joda.time.Instant import timber.log.Timber -import java.io.File -import java.util.UUID import java.util.concurrent.TimeUnit class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor( @@ -49,21 +47,25 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor( @Assisted private val exampleArg: String?, @AppContext private val context: Context, // App context dispatcherProvider: DispatcherProvider, - private val enfClient: ENFClient, - private val riskLevels: RiskLevels, private val taskController: TaskController, private val keyCacheRepository: KeyCacheRepository, private val appConfigProvider: AppConfigProvider, tracingCardStateProvider: TracingCardStateProvider, + @BaseGson private val gson: Gson, private val exposureResultStore: ExposureResultStore ) : CWAViewModel( dispatcherProvider = dispatcherProvider ) { + init { + Timber.d("CWAViewModel: %s", this) + Timber.d("SavedStateHandle: %s", handle) + Timber.d("Example arg: %s", exampleArg) + } + val startLocalQRCodeScanEvent = SingleLiveEvent<Unit>() val riskLevelResetEvent = SingleLiveEvent<Unit>() - val apiKeysProvidedEvent = SingleLiveEvent<DiagnosisKeyProvidedEvent>() - val riskScoreState = MutableLiveData<RiskScoreState>(RiskScoreState()) + val showRiskStatusCard = SubmissionRepository.deviceUIStateFlow.map { it.withSuccess(false) { true } }.asLiveData(dispatcherProvider.Default) @@ -72,13 +74,79 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor( .sample(150L) .asLiveData(dispatcherProvider.Default) - init { - Timber.d("CWAViewModel: %s", this) - Timber.d("SavedStateHandle: %s", handle) - Timber.d("Example arg: %s", exampleArg) - } + val exposureWindowCountString = exposureResultStore + .entities + .map { "Retrieved ${it.exposureWindows.size} Exposure Windows" } + .asLiveData() + + val exposureWindows = exposureResultStore + .entities + .map { if (it.exposureWindows.isEmpty()) "Exposure windows list is empty" else gson.toJson(it.exposureWindows) } + .asLiveData() + + val aggregatedRiskResult = exposureResultStore + .entities + .map { if (it.aggregatedRiskResult != null) it.aggregatedRiskResult.toReadableString() else "Aggregated risk result is not available" } + .asLiveData() + + private fun AggregatedRiskResult.toReadableString(): String = StringBuilder() + .appendLine("Total RiskLevel: $totalRiskLevel") + .appendLine("Total Minimum Distinct Encounters With High Risk: $totalMinimumDistinctEncountersWithHighRisk") + .appendLine("Total Minimum Distinct Encounters With Low Risk: $totalMinimumDistinctEncountersWithLowRisk") + .appendLine("Most Recent Date With High Risk: $mostRecentDateWithHighRisk") + .appendLine("Most Recent Date With Low Risk: $mostRecentDateWithLowRisk") + .appendLine("Number of Days With High Risk: $numberOfDaysWithHighRisk") + .appendLine("Number of Days With Low Risk: $numberOfDaysWithLowRisk") + .toString() + + val backendParameters = appConfigProvider + .currentConfig + .map { it.toReadableString() } + .asLiveData() + + private fun ConfigData.toReadableString(): String = StringBuilder() + .appendLine("Transmission RiskLevel Multiplier: $transmissionRiskLevelMultiplier") + .appendLine() + .appendLine("Minutes At Attenuation Filters:") + .appendLine(minutesAtAttenuationFilters) + .appendLine() + .appendLine("Minutes At Attenuation Weights:") + .appendLine(minutesAtAttenuationWeights) + .appendLine() + .appendLine("Transmission RiskLevel Encoding:") + .appendLine(transmissionRiskLevelEncoding) + .appendLine() + .appendLine("Transmission RiskLevel Filters:") + .appendLine(transmissionRiskLevelFilters) + .appendLine() + .appendLine("Normalized Time Per Exposure Window To RiskLevel Mapping:") + .appendLine(normalizedTimePerExposureWindowToRiskLevelMapping) + .appendLine() + .appendLine("Normalized Time Per Day To RiskLevel Mapping List:") + .appendLine(normalizedTimePerDayToRiskLevelMappingList) + .toString() + + // Only update when risk level gets updated + val additionalRiskCalcInfo = RiskLevelRepository + .riskLevelScore + .map { createAdditionalRiskCalcInfo(it) } + .asLiveData() + + private suspend fun createAdditionalRiskCalcInfo(riskLevelScore: Int): String = StringBuilder() + .appendLine("Risk Level: ${RiskLevel.forValue(riskLevelScore)}") + .appendLine("Last successful Risk Level: ${RiskLevelRepository.getLastSuccessfullyCalculatedScore()}") + .appendLine("Last Time Server Fetch: ${LocalData.lastTimeDiagnosisKeysFromServerFetch()}") + .appendLine("Tracing Duration: ${TimeUnit.MILLISECONDS.toDays(TimeVariables.getTimeActiveTracingDuration())} days") + .appendLine("Tracing Duration in last 14 days: ${TimeVariables.getActiveTracingDaysInRetentionPeriod()} days") + .appendLine( + "Last time risk level calculation ${ + LocalData.lastTimeRiskLevelCalculation()?.let { Instant.ofEpochMilli(it) } + }" + ) + .toString() fun retrieveDiagnosisKeys() { + Timber.d("Starting download diagnosis keys task") launch { taskController.submitBlocking( DefaultTaskRequest( @@ -87,11 +155,11 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor( originTag = "TestRiskLevelCalculationFragmentCWAViewModel.retrieveDiagnosisKeys()" ) ) - calculateRiskLevel() } } fun calculateRiskLevel() { + Timber.d("Starting calculate risk task") taskController.submit( DefaultTaskRequest( RiskLevelTask::class, @@ -101,6 +169,7 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor( } fun resetRiskLevel() { + Timber.d("Resetting risk level") launch { withContext(Dispatchers.IO) { try { @@ -125,117 +194,8 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor( } } - data class RiskScoreState( - val riskScoreMsg: String = "", - val backendParameters: String = "", - val exposureSummary: String = "", - val formula: String = "", - val exposureInfo: String = "" - ) - - fun startENFObserver() { - launch { - try { - val appConfig = appConfigProvider.getAppConfig() - - var workState = riskScoreState.value!! - - val exposureWindows = enfClient.exposureWindows() - - val riskResultsPerWindow = - exposureWindows.mapNotNull { window -> - riskLevels.calculateRisk(appConfig, window)?.let { window to it } - }.toMap() - - val aggregatedResult = riskLevels.aggregateResults(appConfig, riskResultsPerWindow) - - val riskAsString = "Level: ${RiskLevelRepository.getLastCalculatedScore()}\n" + - "Last successful Level: " + - "${LocalData.lastSuccessfullyCalculatedRiskLevel()}\n" + - "Calculated Score: ${aggregatedResult}\n" + - "Last Time Server Fetch: ${LocalData.lastTimeDiagnosisKeysFromServerFetch()}\n" + - "Tracing Duration: " + - "${TimeUnit.MILLISECONDS.toDays(TimeVariables.getTimeActiveTracingDuration())} days \n" + - "Tracing Duration in last 14 days: " + - "${TimeVariables.getActiveTracingDaysInRetentionPeriod()} days \n" + - "Last time risk level calculation ${LocalData.lastTimeRiskLevelCalculation()}" - - workState = workState.copy(riskScoreMsg = riskAsString) - - val configAsString = - "Transmission RiskLevel Multiplier: ${appConfig.transmissionRiskLevelMultiplier}\n" + - "Minutes At Attenuation Filters: ${appConfig.minutesAtAttenuationFilters}\n" + - "Minutes At Attenuation Weights: ${appConfig.minutesAtAttenuationWeights}" + - "Transmission RiskLevel Encoding: ${appConfig.transmissionRiskLevelEncoding}" + - "Transmission RiskLevel Filters: ${appConfig.transmissionRiskLevelFilters}" + - "Normalized Time Per Exposure Window To RiskLevel Mapping: ${appConfig.normalizedTimePerExposureWindowToRiskLevelMapping}" + - "Normalized Time Per Day To RiskLevel Mapping List: ${appConfig.normalizedTimePerDayToRiskLevelMappingList}" - workState = workState.copy(backendParameters = configAsString) - - val summaryAsString = - "Total RiskLevel: ${aggregatedResult.totalRiskLevel}" + - "Total Minimum Distinct Encounters With High Risk: ${aggregatedResult.totalMinimumDistinctEncountersWithHighRisk}" + - "Total Minimum Distinct Encounters With Low Risk: ${aggregatedResult.totalMinimumDistinctEncountersWithLowRisk}" + - "Most Recent Date With High Risk: ${aggregatedResult.mostRecentDateWithHighRisk}" + - "Most Recent Date With Low Risk: ${aggregatedResult.mostRecentDateWithLowRisk}" - - workState = workState.copy(exposureSummary = summaryAsString) - - riskScoreState.postValue(workState) - } catch (e: Exception) { - e.report(ExceptionCategory.EXPOSURENOTIFICATION) - } - } - } - - data class DiagnosisKeyProvidedEvent( - val keyCount: Int - ) - - fun provideDiagnosisKey(transmissionNumber: Int, key: AppleLegacyKeyExchange.Key) { - val appleKeyList = mutableListOf<AppleLegacyKeyExchange.Key>() - - AppleLegacyKeyExchange.Key.newBuilder() - .setKeyData(key.keyData) - .setRollingPeriod(144) - .setRollingStartNumber(key.rollingStartNumber) - .setTransmissionRiskLevel(transmissionNumber) - .build() - .also { appleKeyList.add(it) } - - val appleFiles = listOf( - AppleLegacyKeyExchange.File.newBuilder() - .addAllKeys(appleKeyList) - .build() - ) - - val dir = File(File(context.getExternalFilesDir(null), "key-export"), UUID.randomUUID().toString()) - dir.mkdirs() - - var googleFileList: List<File> - launch { - googleFileList = KeyFileHelper.asyncCreateExportFiles(appleFiles, dir) - - Timber.i("Provide ${googleFileList.count()} files with ${appleKeyList.size} keys") - try { - // only testing implementation: this is used to wait for the broadcastreceiver of the OS / EN API - enfClient.provideDiagnosisKeys(googleFileList) - apiKeysProvidedEvent.postValue( - DiagnosisKeyProvidedEvent( - keyCount = appleFiles.size - ) - ) - } catch (e: Exception) { - e.report(ExceptionCategory.EXPOSURENOTIFICATION) - } - } - } - - fun scanLocalQRCodeAndProvide() { - startLocalQRCodeScanEvent.postValue(Unit) - } - fun clearKeyCache() { + Timber.d("Clearing key cache") launch { keyCacheRepository.clear() } } diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_risk_level_calculation.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_risk_level_calculation.xml index a58a1ff43..7d94e7c3d 100644 --- a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_risk_level_calculation.xml +++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_risk_level_calculation.xml @@ -32,7 +32,6 @@ <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="@dimen/spacing_normal" android:orientation="vertical"> <TextView @@ -60,34 +59,6 @@ </FrameLayout> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/spacing_normal" - android:orientation="horizontal"> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Transmission Risk Level for scan: " /> - - <EditText - android:id="@+id/transmission_number" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:ems="10" - android:inputType="number" - android:text="5" /> - </LinearLayout> - - <Button - android:id="@+id/button_provide_key_via_qr" - style="@style/buttonPrimary" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/spacing_normal" - android:text="Scan Local QR Code" /> - <Button android:id="@+id/button_retrieve_diagnosis_keys" style="@style/buttonPrimary" @@ -121,30 +92,30 @@ android:text="Clear Diagnosis-Key cache" /> <TextView - android:id="@+id/label_exposure_summary_title" + android:id="@+id/label_aggregated_risk_result_title" style="@style/headline6" android:accessibilityHeading="true" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_normal" - android:text="Exposure Summary" /> + android:text="Aggregated Risk Result" /> <TextView - android:id="@+id/label_exposure_summary" + android:id="@+id/label_aggregated_risk_result" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="-" /> <TextView - android:id="@+id/label_risk_score_title" + android:id="@+id/label_risk_additional_info_title" style="@style/headline6" android:accessibilityHeading="true" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="Risk Score" /> + android:text="Risk Calculation Additional Information" /> <TextView - android:id="@+id/label_risk_score" + android:id="@+id/label_risk_additional_info" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="-" /> @@ -164,31 +135,24 @@ android:text="-" /> <TextView - android:id="@+id/label_formula_title" + android:id="@+id/label_exposure_window_title" style="@style/headline6" android:accessibilityHeading="true" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="Used Formula" /> + android:text="Exposure Windows" /> <TextView - android:id="@+id/label_formula" + android:id="@+id/label_exposure_window_count" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="-" /> <TextView - android:id="@+id/label_exposure_info_title" - style="@style/headline6" - android:accessibilityHeading="true" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="Exposure Information" /> - - <TextView - android:id="@+id/label_exposure_info" - android:layout_width="match_parent" + android:id="@+id/label_exposure_windows" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:paddingTop="5dp" android:text="-" /> </LinearLayout> diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/RiskLevelRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/RiskLevelRepository.kt index 7b5b24702..62db1f8e7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/RiskLevelRepository.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/RiskLevelRepository.kt @@ -80,7 +80,7 @@ object RiskLevelRepository { * * @return */ - private fun getLastSuccessfullyCalculatedScore(): RiskLevel = + fun getLastSuccessfullyCalculatedScore(): RiskLevel = LocalData.lastSuccessfullyCalculatedRiskLevel() /** -- GitLab