diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt index 9f492814b0eb1d8ef60306c2a4bd7d6b1e5fc8fd..94df6e4ab1c9a8c8663c845b92a2dc64a1c420da 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt @@ -258,7 +258,9 @@ class TestRiskLevelCalculation : Fragment() { "Tracing Duration: " + "${TimeUnit.MILLISECONDS.toDays(TimeVariables.getTimeActiveTracingDuration())} days \n" + "Tracing Duration in last 14 days: " + - "${TimeVariables.getActiveTracingDaysInRetentionPeriod()} days" + "${TimeVariables.getActiveTracingDaysInRetentionPeriod()} days \n" + + "Last time risk level calculation ${LocalData.lastTimeRiskLevelCalculation()}" + binding.labelRiskScore.text = riskAsString val lowClass = diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt index c6c6bba9de200e336b15b2a8022d1cd54cff6ef7..fc0d79de67212171d3380e3444219f12911feb1c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt @@ -276,7 +276,6 @@ object LocalData { * @param value timestamp as Date */ fun lastTimeDiagnosisKeysFromServerFetch(value: Date?) { - // TODO need this for nullable ref, shout not be goto for nullable storage getSharedPreferenceInstance().edit(true) { putLong( CoronaWarnApplication.getAppContext() @@ -286,6 +285,37 @@ object LocalData { } } + /** + * Gets the last time of successful risk level calculation as long + * from the EncryptedSharedPrefs + * + * @return Long + */ + fun lastTimeRiskLevelCalculation(): Long? { + val time = getSharedPreferenceInstance().getLong( + CoronaWarnApplication.getAppContext() + .getString(R.string.preference_timestamp_risk_level_calculation), + 0L + ) + return Date(time).time + } + + /** + * Sets the last time of successful risk level calculation as long + * from the EncryptedSharedPrefs + * + * @param value timestamp as Long + */ + fun lastTimeRiskLevelCalculation(value: Long?) { + getSharedPreferenceInstance().edit(true) { + putLong( + CoronaWarnApplication.getAppContext() + .getString(R.string.preference_timestamp_risk_level_calculation), + value ?: 0L + ) + } + } + /** * Gets the last timestamp the user manually triggered the key retrieval process * diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RiskLevelTransaction.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RiskLevelTransaction.kt index df32874cc6044d524cff9c095c2d15f946c2d3ec..690595b1ff994657c94d564fddf2d97108fa3356 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RiskLevelTransaction.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RiskLevelTransaction.kt @@ -32,6 +32,7 @@ import de.rki.coronawarnapp.transaction.RiskLevelTransaction.RiskLevelTransactio import de.rki.coronawarnapp.transaction.RiskLevelTransaction.RiskLevelTransactionState.CLOSE import de.rki.coronawarnapp.transaction.RiskLevelTransaction.RiskLevelTransactionState.RETRIEVE_APPLICATION_CONFIG import de.rki.coronawarnapp.transaction.RiskLevelTransaction.RiskLevelTransactionState.RETRIEVE_EXPOSURE_SUMMARY +import de.rki.coronawarnapp.transaction.RiskLevelTransaction.RiskLevelTransactionState.RISK_CALCULATION_DATE_UPDATE import de.rki.coronawarnapp.transaction.RiskLevelTransaction.RiskLevelTransactionState.UPDATE_RISK_LEVEL import de.rki.coronawarnapp.util.TimeAndDateExtensions.millisecondsToHours import kotlinx.coroutines.Dispatchers @@ -108,7 +109,8 @@ import java.util.concurrent.atomic.AtomicReference * 6. [CHECK_INCREASED_RISK] * 7. [CHECK_UNKNOWN_RISK_INITIAL_TRACING_DURATION] * 8. [UPDATE_RISK_LEVEL] - * 9. [CLOSE] + * 9. [RISK_CALCULATION_DATE_UPDATE] + * 10. [CLOSE] * * This transaction will queue up any start calls and executes them in the given order (unlike the other defined * transactions (e.g. [RetrieveDiagnosisKeysTransaction]). This is necessary in order to respond to various trigger @@ -152,6 +154,9 @@ object RiskLevelTransaction : Transaction() { /** Update and persist the Risk Level Score with the calculated score */ UPDATE_RISK_LEVEL, + /** Update of the Date to reflect a complete Transaction State */ + RISK_CALCULATION_DATE_UPDATE, + /** Transaction Closure */ CLOSE } @@ -170,6 +175,9 @@ object RiskLevelTransaction : Transaction() { /** atomic reference for the rollback value for the last calculated risk level score */ private val lastCalculatedRiskLevelScoreForRollback = AtomicReference<RiskLevel>() + /** atomic reference for the rollback value for date of last risk level calculation */ + private val lastCalculatedRiskLevelDate = AtomicReference<Long>() + /** initiates the transaction. This suspend function guarantees a successful transaction once completed. */ suspend fun start() = lockAndExecute { /**************************************************** @@ -220,6 +228,7 @@ object RiskLevelTransaction : Transaction() { if (result == UNDETERMINED) { lastCalculatedRiskLevelScoreForRollback.set(RiskLevelRepository.getLastCalculatedScore()) executeUpdateRiskLevelScore(LOW_LEVEL_RISK) + executeRiskLevelCalculationDateUpdate() executeClose() return@lockAndExecute } else { @@ -233,6 +242,9 @@ object RiskLevelTransaction : Transaction() { if (UPDATE_RISK_LEVEL.isInStateStack()) { updateRiskLevelScore(lastCalculatedRiskLevelScoreForRollback.get()) } + if (RISK_CALCULATION_DATE_UPDATE.isInStateStack()) { + LocalData.lastTimeRiskLevelCalculation(lastCalculatedRiskLevelDate.get()) + } } catch (e: Exception) { // We handle every exception through a RollbackException to make sure that a single EntryPoint // is available for the caller. @@ -261,7 +273,10 @@ object RiskLevelTransaction : Transaction() { // if there was no key retrieval before, we return no calculation state TimeVariables.getLastTimeDiagnosisKeysFromServerFetch() ?: return@executeState UNKNOWN_RISK_INITIAL.also { - Log.v(TAG, "$transactionId - no last time diagnosis keys from server fetch timestamp was found") + Log.v( + TAG, + "$transactionId - no last time diagnosis keys from server fetch timestamp was found" + ) } Log.v(TAG, "$transactionId - CHECK_UNKNOWN_RISK_INITIAL_NO_KEYS not applicable") @@ -314,15 +329,16 @@ object RiskLevelTransaction : Transaction() { /** * Executes the [RETRIEVE_EXPOSURE_SUMMARY] Transaction State */ - private suspend fun executeRetrieveExposureSummary(): ExposureSummary = executeState(RETRIEVE_EXPOSURE_SUMMARY) { - val lastExposureSummary = getLastExposureSummary() ?: getNewExposureSummary() + private suspend fun executeRetrieveExposureSummary(): ExposureSummary = + executeState(RETRIEVE_EXPOSURE_SUMMARY) { + val lastExposureSummary = getLastExposureSummary() ?: getNewExposureSummary() - return@executeState lastExposureSummary.also { - // todo remove after testing sessions - recordedTransactionValuesForTestingOnly.exposureSummary = it - Log.v(TAG, "$transactionId - get the exposure summary for further calculation") + return@executeState lastExposureSummary.also { + // todo remove after testing sessions + recordedTransactionValuesForTestingOnly.exposureSummary = it + Log.v(TAG, "$transactionId - get the exposure summary for further calculation") + } } - } /** * Executes the [CHECK_INCREASED_RISK] Transaction State @@ -412,6 +428,7 @@ object RiskLevelTransaction : Transaction() { private suspend fun executeClose() = executeState(CLOSE) { Log.v(TAG, "$transactionId - transaction will close") lastCalculatedRiskLevelScoreForRollback.set(null) + lastCalculatedRiskLevelDate.set(null) } /**************************************************** @@ -435,6 +452,8 @@ object RiskLevelTransaction : Transaction() { ) lastCalculatedRiskLevelScoreForRollback.set(RiskLevelRepository.getLastCalculatedScore()) executeUpdateRiskLevelScore(riskLevel) + lastCalculatedRiskLevelDate.set(LocalData.lastTimeRiskLevelCalculation()) + executeRiskLevelCalculationDateUpdate() executeClose() return true } @@ -522,4 +541,14 @@ object RiskLevelTransaction : Transaction() { Log.v(TAG, "$transactionId - generated new exposure summary with $googleToken") } } + + /** + * Executes the CALCULATION_DATE_UPDATE Transaction State + */ + private suspend fun executeRiskLevelCalculationDateUpdate() { + val currentDate = System.currentTimeMillis() + executeState(RISK_CALCULATION_DATE_UPDATE) { + LocalData.lastTimeRiskLevelCalculation(currentDate) + } + } } diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml index e24cb16b0d903f6c1631f692e448caaacbd6d231..27ba1c703b75230fe72cc2451398f43f455b9b99 100644 --- a/Corona-Warn-App/src/main/res/values/strings.xml +++ b/Corona-Warn-App/src/main/res/values/strings.xml @@ -85,6 +85,10 @@ <xliff:g id="preference">preference_risk_level_score_successful</xliff:g> </string> <!-- NOTR --> + <string name="preference_timestamp_risk_level_calculation"> + <xliff:g id="preference">preference_timestamp_risk_level_calculation</xliff:g> + </string> + <!-- NOTR --> <string name="preference_test_guid"> <xliff:g id="preference">preference_test_guid</xliff:g> </string> diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RiskLevelTransactionTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RiskLevelTransactionTest.kt index e38eecbc249dcaf85442458bc03919307b6b9eff..550851bd095803eb7ff15cc2bb7d2e15eadd4f09 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RiskLevelTransactionTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RiskLevelTransactionTest.kt @@ -51,12 +51,15 @@ class RiskLevelTransactionTest { mockkObject(ExposureSummaryRepository.Companion) mockkObject(RiskLevel.Companion) + every { ExposureSummaryRepository.getExposureSummaryRepository() } returns esRepositoryMock every { RiskLevelRepository.getLastCalculatedScore() } returns UNDETERMINED every { RiskLevelRepository.setRiskLevelScore(any()) } just Runs every { RiskLevel.riskLevelChangedBetweenLowAndHigh(any(), any()) } returns false + every { LocalData.lastTimeRiskLevelCalculation() } returns System.currentTimeMillis() + every { LocalData.lastTimeRiskLevelCalculation(any()) } just Runs } /** Test case for [NO_CALCULATION_POSSIBLE_TRACING_OFF] */ @@ -79,6 +82,7 @@ class RiskLevelTransactionTest { RiskLevelTransaction["isValidResult"](testRiskLevel) RiskLevelRepository.setRiskLevelScore(testRiskLevel) + RiskLevelTransaction["executeRiskLevelCalculationDateUpdate"]() RiskLevelTransaction["executeClose"]() } } @@ -110,6 +114,7 @@ class RiskLevelTransactionTest { RiskLevelTransaction["isValidResult"](testRiskLevel) RiskLevelRepository.setRiskLevelScore(testRiskLevel) + RiskLevelTransaction["executeRiskLevelCalculationDateUpdate"]() RiskLevelTransaction["executeClose"]() } } @@ -153,6 +158,7 @@ class RiskLevelTransactionTest { RiskLevelTransaction["isValidResult"](testRiskLevel) RiskLevelRepository.setRiskLevelScore(testRiskLevel) + RiskLevelTransaction["executeRiskLevelCalculationDateUpdate"]() RiskLevelTransaction["executeClose"]() } } @@ -209,6 +215,7 @@ class RiskLevelTransactionTest { RiskLevelTransaction["isValidResult"](testRiskLevel) RiskLevelRepository.setRiskLevelScore(testRiskLevel) + RiskLevelTransaction["executeRiskLevelCalculationDateUpdate"]() RiskLevelTransaction["executeClose"]() } } @@ -271,6 +278,7 @@ class RiskLevelTransactionTest { RiskLevelTransaction["isValidResult"](testRiskLevel) RiskLevelRepository.setRiskLevelScore(testRiskLevel) + RiskLevelTransaction["executeRiskLevelCalculationDateUpdate"]() RiskLevelTransaction["executeClose"]() } } @@ -332,6 +340,7 @@ class RiskLevelTransactionTest { RiskLevelTransaction["isValidResult"](UNDETERMINED) RiskLevelRepository.setRiskLevelScore(testRiskLevel) + RiskLevelTransaction["executeRiskLevelCalculationDateUpdate"]() RiskLevelTransaction["executeClose"]() } }