Attention une mise à jour du service Gitlab va être effectuée le mardi 18 janvier (et non lundi 17 comme annoncé précédemment) entre 18h00 et 18h30. Cette mise à jour va générer une interruption du service dont nous ne maîtrisons pas complètement la durée mais qui ne devrait pas excéder quelques minutes.

Commit b7ce17d2 authored by stopcovid@lunabee.com's avatar stopcovid@lunabee.com
Browse files

Update to 2.1.4

parent 2022f41e
......@@ -30,7 +30,8 @@ object UiConstants {
UPGRADE("upgrade", 4),
TIME("error", 5),
BLUETOOTH("error", 6),
NEWS("news", 7)
NEWS("news", 7),
REMINDER("reminder", 8)
}
const val DEFAULT_LANGUAGE: String = "en"
......
......@@ -56,4 +56,16 @@ fun @receiver:AttrRes Int.fetchSystemColorStateList(context: Context): ColorStat
* @return Resource dimension value multiplied by the appropriate metric.
*/
@Dimension
fun @receiver:DimenRes Int.toDimensSize(context: Context): Float = context.resources.getDimension(this)
\ No newline at end of file
fun @receiver:DimenRes Int.toDimensSize(context: Context): Float = context.resources.getDimension(this)
/**
* Return the id of the resource from the receiver id.
*
* @param context context where the theme is.
* @return id of the resource.
*/
fun @receiver:AttrRes Int.resolveAttribute(context: Context): Int {
val outValue = TypedValue()
context.theme.resolveAttribute(this, outValue, true)
return outValue.resourceId
}
\ No newline at end of file
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* Authors
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Created by Lunabee Studio / Date - 2020/12/11 - for the STOP-COVID project
*/
package com.lunabeestudio.stopcovid.coreui.extension
import timber.log.Timber
import java.util.IllegalFormatException
fun Map<String, String>.stringsFormat(key: String, vararg args: Any?): String? {
return this[key]?.let {
try {
String.format(it, *args)
} catch (e: IllegalFormatException) {
Timber.e(e)
it
}
}
}
\ No newline at end of file
......@@ -11,12 +11,25 @@
package com.lunabeestudio.stopcovid.coreui.fastitem
import android.view.View
import androidx.annotation.DimenRes
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.RecyclerView
import com.lunabeestudio.stopcovid.coreui.R
import com.lunabeestudio.stopcovid.coreui.extension.toDimensSize
class DividerItem : BaseItem<DividerItem.ViewHolder>(
R.layout.item_divider, DividerItem::ViewHolder, R.id.item_divider
) {
@DimenRes
var marginStartRes: Int? = R.dimen.spacing_large
override fun bindView(holder: ViewHolder, payloads: List<Any>) {
super.bindView(holder, payloads)
holder.itemView.updateLayoutParams<RecyclerView.LayoutParams> {
marginStart = marginStartRes?.toDimensSize(holder.itemView.context)?.toInt() ?: 0
}
}
class ViewHolder(v: View) : RecyclerView.ViewHolder(v)
}
......
......@@ -15,9 +15,8 @@ import android.os.Bundle
import android.text.format.DateUtils
import android.view.View
import androidx.fragment.app.Fragment
import com.lunabeestudio.stopcovid.coreui.extension.stringsFormat
import com.lunabeestudio.stopcovid.coreui.manager.StringsManager
import timber.log.Timber
import java.util.IllegalFormatException
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
import kotlin.time.days
......@@ -39,14 +38,7 @@ abstract class BaseFragment : Fragment() {
}
protected fun stringsFormat(key: String, vararg args: Any?): String? {
return strings[key]?.let {
try {
String.format(it, *args)
} catch (e: IllegalFormatException) {
Timber.e(e)
it
}
}
return strings.stringsFormat(key, *args)
}
@OptIn(ExperimentalTime::class)
......
......@@ -36,6 +36,7 @@ abstract class ServerManager {
protected open fun transform(input: String): String = input
protected open fun extension(): String = ".json"
protected open fun url(): String = BuildConfig.SERVER_URL
protected open fun urlFolderName(): String = ""
protected suspend fun fetchLast(context: Context, forceRefresh: Boolean): Boolean {
return if (shouldRefresh(context) || forceRefresh) {
......@@ -58,7 +59,7 @@ abstract class ServerManager {
private suspend fun fetchLast(context: Context, languageCode: String): Boolean {
return try {
val filename = "${prefix(context)}${languageCode}${extension()}"
"${url()}$filename".saveTo(context, File(context.filesDir, filename))
"${url()}${urlFolderName()}$filename".saveTo(context, File(context.filesDir, filename))
saveLastRefresh(context)
true
} catch (e: Exception) {
......
......@@ -8,7 +8,6 @@
~ Created by Lunabee Studio / Date - 2020/13/05 - for the TOUS-ANTI-COVID project
-->
<resources>
<color name="color_primary">#85BBFF</color>
<color name="color_on_primary">#00285A</color>
<color name="color_primary_light">#2685BBFF</color>
<color name="color_on_primary_light">#FFFFFF</color>
......
......@@ -10,6 +10,7 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.StopCovid" parent="Theme.Base.StopCovid">
<item name="colorPrimary">@color/color_malibu</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
</style>
......
......@@ -12,7 +12,7 @@
<style name="Widget.StopCovid.Button.Light">
<item name="backgroundTint">@color/color_primary_light</item>
<item name="android:textColor">@color/color_on_primary_light</item>
<item name="rippleColor">@color/color_primary</item>
<item name="rippleColor">?colorPrimary</item>
</style>
<style name="Widget.StopCovid.Button.Danger" parent="Widget.StopCovid.Button.Light">
......
......@@ -8,7 +8,6 @@
~ Created by Lunabee Studio / Date - 2020/04/05 - for the TOUS-ANTI-COVID project
-->
<resources>
<color name="color_primary">#5770BE</color>
<color name="color_on_primary">#FFFFFF</color>
<color name="color_primary_light">#1A000091</color>
<color name="color_on_primary_light">#000091</color>
......@@ -20,6 +19,7 @@
<color name="color_icon_background">#FFFFFF</color>
<color name="color_on_gradient">#FFFFFF</color>
<color name="color_indigo">#5770BE</color>
<color name="color_no_risk">#449976</color>
<color name="color_risk">#fd706a</color>
<color name="color_on_status_notification">#FFFFFF</color>
......@@ -30,4 +30,6 @@
<color name="color_monza">#E1000F</color>
<color name="color_east_bay">#484D7A</color>
<color name="color_neon_carrot">#FF9940</color>
<color name="color_persian_blue">#0F31C8</color>
<color name="color_malibu">#85BBFF</color>
</resources>
......@@ -13,7 +13,7 @@
<item name="android:windowBackground">?colorSurface</item>
<item name="android:windowSoftInputMode">adjustResize</item>
<item name="colorPrimary">@color/color_primary</item>
<item name="colorPrimary">@color/color_indigo</item>
<item name="colorPrimaryVariant">?colorPrimary</item>
<item name="colorPrimaryDark">?colorPrimary</item>
<item name="colorOnPrimary">@color/color_on_primary</item>
......
......@@ -42,7 +42,7 @@
<item name="elevation">0dp</item>
<item name="rippleColor">@color/color_primary_light</item>
<item name="android:textAllCaps">false</item>
<item name="android:textColor">@color/color_primary</item>
<item name="android:textColor">?colorPrimary</item>
<item name="android:textSize">@dimen/button_font_size</item>
<item name="android:letterSpacing">@dimen/button_letter_spacing</item>
</style>
......@@ -51,7 +51,7 @@
<item name="android:padding">@dimen/spacing_large</item>
<item name="rippleColor">@color/color_primary_light</item>
<item name="android:textAllCaps">false</item>
<item name="android:textColor">@color/color_primary</item>
<item name="android:textColor">?colorPrimary</item>
<item name="android:textSize">@dimen/button_font_size</item>
<item name="android:letterSpacing">@dimen/button_letter_spacing</item>
</style>
......
......@@ -115,6 +115,25 @@ class SecureKeystoreDataSource(context: Context, private val cryptoManager: Loca
}
}
override var atRiskLastError: Long?
get() {
val encryptedText = sharedPreferences.getString(SHARED_PREF_KEY_AT_RISK_LAST_ERROR, null)
return if (encryptedText != null) {
cryptoManager.decryptToString(encryptedText).toLongOrNull()
} else {
null
}
}
set(value) {
if (value != null) {
sharedPreferences.edit()
.putString(SHARED_PREF_KEY_AT_RISK_LAST_ERROR, cryptoManager.encryptToString(value.toString()))
.apply()
} else {
sharedPreferences.edit().remove(SHARED_PREF_KEY_AT_RISK_LAST_ERROR).apply()
}
}
override var atRiskMinHourContactNotif: Int?
get() {
val encryptedText = sharedPreferences.getString(SHARED_PREF_KEY_AT_RISK_MIN_HOUR_CONTACT_NOTIF, null)
......@@ -401,6 +420,25 @@ class SecureKeystoreDataSource(context: Context, private val cryptoManager: Loca
}
}
override var minStatusRetryDuraction: Float?
get() {
val encryptedText = sharedPreferences.getString(SHARED_PREF_KEY_MIN_STATUS_RETRY_DURATION, null)
return if (encryptedText != null) {
cryptoManager.decryptToString(encryptedText).toFloatOrNull()
} else {
null
}
}
set(value) {
if (value != null) {
sharedPreferences.edit()
.putString(SHARED_PREF_KEY_MIN_STATUS_RETRY_DURATION, cryptoManager.encryptToString(value.toString()))
.apply()
} else {
sharedPreferences.edit().remove(SHARED_PREF_KEY_MIN_STATUS_RETRY_DURATION).apply()
}
}
override var randomStatusHour: Float?
get() {
val encryptedText = sharedPreferences.getString(SHARED_PREF_KEY_RANDOM_STATUS_HOUR, null)
......@@ -439,6 +477,25 @@ class SecureKeystoreDataSource(context: Context, private val cryptoManager: Loca
}
}
override var positiveSampleSpan: Int?
get() {
val encryptedText = sharedPreferences.getString(SHARED_PREF_KEY_POSITIVE_SAMPLE_SPAN, null)
return if (encryptedText != null) {
cryptoManager.decryptToString(encryptedText).toIntOrNull()
} else {
null
}
}
set(value) {
if (value != null) {
sharedPreferences.edit()
.putString(SHARED_PREF_KEY_POSITIVE_SAMPLE_SPAN, cryptoManager.encryptToString(value.toString()))
.apply()
} else {
sharedPreferences.edit().remove(SHARED_PREF_KEY_POSITIVE_SAMPLE_SPAN).apply()
}
}
override var appAvailability: Boolean?
get() {
val encryptedText = sharedPreferences.getString(SHARED_PREF_KEY_APP_AVAILABILITY, null)
......@@ -591,7 +648,26 @@ class SecureKeystoreDataSource(context: Context, private val cryptoManager: Loca
}
}
var saveAttestationData: Boolean?
override var proximityReactivationReminderHours: List<Int>?
get() {
val encryptedText = sharedPreferences.getString(SHARED_PREF_KEY_PROXIMITY_REACTIVATE_REMINDER_HOURS, null)
return if (encryptedText != null) {
Gson().fromJson(cryptoManager.decryptToString(encryptedText), object : TypeToken<List<Int>>() {}.type)
} else {
null
}
}
set(value) {
if (value != null) {
sharedPreferences.edit()
.putString(SHARED_PREF_KEY_PROXIMITY_REACTIVATE_REMINDER_HOURS, cryptoManager.encryptToString(Gson().toJson(value)))
.apply()
} else {
sharedPreferences.edit().remove(SHARED_PREF_KEY_PROXIMITY_REACTIVATE_REMINDER_HOURS).apply()
}
}
override var saveAttestationData: Boolean?
get() {
val encryptedText = sharedPreferences.getString(SHARED_PREF_KEY_SAVE_ATTESTATION_DATA, null)
return if (encryptedText != null) {
......@@ -610,7 +686,7 @@ class SecureKeystoreDataSource(context: Context, private val cryptoManager: Loca
}
}
var savedAttestationData: Map<String, FormEntry>?
override var savedAttestationData: Map<String, FormEntry>?
get() {
val encryptedText = sharedPreferences.getString(SHARED_PREF_KEY_SAVED_ATTESTATION_DATA, null)
return if (encryptedText != null) {
......@@ -630,8 +706,9 @@ class SecureKeystoreDataSource(context: Context, private val cryptoManager: Loca
}
private var _attestationsLiveData: MutableLiveData<List<Map<String, FormEntry>>?> = MutableLiveData(attestations)
var attestationsLiveData: LiveData<List<Map<String, FormEntry>>?> = _attestationsLiveData
var attestations: List<Map<String, FormEntry>>?
override val attestationsLiveData: LiveData<List<Map<String, FormEntry>>?>
get() = _attestationsLiveData
override var attestations: List<Map<String, FormEntry>>?
get() {
val encryptedText = sharedPreferences.getString(SHARED_PREF_KEY_ATTESTATIONS, null)
return if (encryptedText != null) {
......@@ -677,6 +754,7 @@ class SecureKeystoreDataSource(context: Context, private val cryptoManager: Loca
private const val SHARED_PREF_KEY_KEA = "shared.pref.kea"
private const val SHARED_PREF_KEY_TIME_START = "shared.pref.time_start"
private const val SHARED_PREF_KEY_AT_RISK_LAST_REFRESH = "shared.pref.at_risk_last_refresh"
private const val SHARED_PREF_KEY_AT_RISK_LAST_ERROR = "shared.pref.at_risk_last_error"
private const val SHARED_PREF_KEY_LAST_RISK_RECEIVED_DATE = "shared.pref.last_risk_received_date"
private const val SHARED_PREF_KEY_AT_RISK_MIN_HOUR_CONTACT_NOTIF = "shared.pref.at_risk_min_hour_contact_notif"
private const val SHARED_PREF_KEY_AT_RISK_MAX_HOUR_CONTACT_NOTIF = "shared.pref.at_risk_max_hour_contact_notif"
......@@ -692,8 +770,10 @@ class SecureKeystoreDataSource(context: Context, private val cryptoManager: Loca
private const val SHARED_PREF_KEY_DATA_RETENTION_PERIOD = "shared.pref.data_retention_period"
private const val SHARED_PREF_KEY_QUARANTINE_PERIOD = "shared.pref.quarantine_period"
private const val SHARED_PREF_KEY_CHECK_STATUS_FREQUENCY = "shared.pref.check_status_frequency"
private const val SHARED_PREF_KEY_MIN_STATUS_RETRY_DURATION = "shared.pref.min_status_retry_duration"
private const val SHARED_PREF_KEY_RANDOM_STATUS_HOUR = "shared.pref.random_status_hour"
private const val SHARED_PREF_KEY_PRE_SYMPTOMS_SPAN = "shared.pref.pre_symptoms_span"
private const val SHARED_PREF_KEY_POSITIVE_SAMPLE_SPAN = "shared.pref.positive_sample_span"
private const val SHARED_PREF_KEY_APP_AVAILABILITY = "shared.pref.app_availability"
private const val SHARED_PREF_KEY_API_VERSION = "shared.pref.api_version"
private const val SHARED_PREF_KEY_QR_CODE_DELETION_HOURS = "shared.pref.qr_code_deletion_hours"
......@@ -701,6 +781,7 @@ class SecureKeystoreDataSource(context: Context, private val cryptoManager: Loca
private const val SHARED_PREF_KEY_QR_CODE_FORMATTED_STRING = "shared.pref.qr_code_formatted_string"
private const val SHARED_PREF_KEY_QR_CODE_FORMATTED_STRING_DISPLAYED = "shared.pref.qr_code_formatted_string_displayed"
private const val SHARED_PREF_KEY_QR_CODE_FOOTER_STRING = "shared.pref.qr_code_footer_string"
private const val SHARED_PREF_KEY_PROXIMITY_REACTIVATE_REMINDER_HOURS = "shared.pref.proximity_reactivate_reminder_hours"
private const val SHARED_PREF_KEY_SAVE_ATTESTATION_DATA = "shared.pref.save_attestation_data"
private const val SHARED_PREF_KEY_SAVED_ATTESTATION_DATA = "shared.pref.saved_attestation_data"
private const val SHARED_PREF_KEY_ATTESTATIONS = "shared.pref.attestations"
......
......@@ -23,6 +23,8 @@ android {
}
kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8.toString()
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}
dependencies {
......
......@@ -21,8 +21,10 @@ object RobertConstant {
const val DATA_RETENTION_PERIOD: Int = 14
const val QUARANTINE_PERIOD: Int = 14
const val CHECK_STATUS_FREQUENCY_HOURS: Float = 24F
const val MIN_STATUS_RETRY_DURATION: Float = 0.6F
const val RANDOM_STATUS_HOUR: Float = 12F
const val PRE_SYMPTOMS_SPAN: Int = 2
const val POSITIVE_SAMPLE_SPAN: Int = 7
const val MIN_HOUR_CONTACT_NOTIF: Int = 7
const val MAX_HOUR_CONTACT_NOTIF: Int = 19
const val BLE_SERVICE_UUID: String = "0000fd64-0000-1000-8000-00805f9b34fb"
......@@ -30,7 +32,6 @@ object RobertConstant {
const val BLE_BACKGROUND_SERVICE_MANUFACTURER_DATA_IOS: String = "1.0.0.0.0.0.0.0.0.0.0.8.0.0.0.0.0"
const val BLE_FILTER_CONFIG: String = "{\"a\":4.3429448190325175,\"b\":0.1,\"deltas\":[39.0,27.0,23.0,21.0,20.0,19.0,18.0,17.0,16.0,15.0],\"durationThreshold\":120,\"p0\":-66.0,\"rssiThreshold\":-25,\"timeOverlap\":60,\"timeWindow\":120,\"riskThreshold\":0.1}"
val BLE_FILTER_MODE: LocalProximityFilter.Mode = LocalProximityFilter.Mode.RISKS
const val MIN_GAP_SUCCESS_STATUS: Long = 30L * 60L * 1000L
const val QR_CODE_DELETION_HOURS: Float = 24F
const val QR_CODE_EXPIRED_HOURS: Float = 1F
const val QR_CODE_FORMATTED_STRING: String = "Cree le: <creationDate> a <creationHour>;\nNom: <lastname>;\nPrenom: <firstname>;\nNaissance: <dob> a <cityofbirth>;\nAdresse: <address> <zip> <city>;\nSortie: <datetime-day> a <datetime-hour>;\nMotif: <reason-code>"
......@@ -43,6 +44,7 @@ object RobertConstant {
const val DATA_RETENTION_PERIOD: String = "app.dataRetentionPeriod"
const val QUARANTINE_PERIOD: String = "app.quarantinePeriod"
const val CHECK_STATUS_FREQUENCY: String = "app.checkStatusFrequency"
const val MIN_STATUS_RETRY_DURATION: String = "app.minStatusRetryDuration"
const val RANDOM_STATUS_HOUR: String = "app.randomStatusHour"
const val PRE_SYMPTOMS_SPAN: String = "app.preSymptomsSpan"
const val APP_AVAILABILITY: String = "app.appAvailability"
......@@ -60,6 +62,7 @@ object RobertConstant {
const val QR_CODE_FORMATTED_STRING: String = "app.qrCode.formattedString"
const val QR_CODE_FORMATTED_STRING_DISPLAYED: String = "app.qrCode.formattedStringDisplayed"
const val QR_CODE_FOOTER_STRING: String = "app.qrCode.footerString"
const val PROXIMITY_REACTICATION_REMINDER_HOURS: String = "app.proximityReactivation.reminderHours"
}
object PREFIX {
......
......@@ -57,6 +57,8 @@ interface RobertManager {
val checkStatusFrequencyHour: Float
val minStatusRetryDuration: Float
val randomStatusHour: Float
val apiVersion: String
......@@ -73,6 +75,8 @@ interface RobertManager {
val displayDepartmentLevel: Boolean
val proximityReactivationReminderHours: List<Int>
suspend fun refreshConfig(application: RobertApplication): RobertResult
suspend fun generateCaptcha(type: String, local: String): RobertResultData<String>
......@@ -102,7 +106,8 @@ interface RobertManager {
suspend fun report(
token: String,
firstSymptoms: Int,
firstSymptoms: Int?,
positiveTest: Int?,
application: RobertApplication
): RobertResult
......
......@@ -60,6 +60,10 @@ import kotlinx.coroutines.withContext
import timber.log.Timber
import java.util.concurrent.TimeUnit
import kotlin.math.abs
import kotlin.math.min
import kotlin.time.Duration
import kotlin.time.DurationUnit
import kotlin.time.ExperimentalTime
class RobertManagerImpl(
application: RobertApplication,
......@@ -152,6 +156,9 @@ class RobertManagerImpl(
override val checkStatusFrequencyHour: Float
get() = keystoreRepository.checkStatusFrequencyHour ?: RobertConstant.CHECK_STATUS_FREQUENCY_HOURS
override val minStatusRetryDuration: Float
get() = keystoreRepository.minStatusRetryDuration ?: RobertConstant.MIN_STATUS_RETRY_DURATION
override val randomStatusHour: Float
get() = keystoreRepository.randomStatusHour ?: RobertConstant.RANDOM_STATUS_HOUR
......@@ -176,6 +183,9 @@ class RobertManagerImpl(
override val displayDepartmentLevel: Boolean
get() = keystoreRepository.displayDepartmentLevel ?: false
override val proximityReactivationReminderHours: List<Int>
get() = keystoreRepository.proximityReactivationReminderHours ?: emptyList()
override suspend fun refreshConfig(application: RobertApplication): RobertResult {
val configResult = remoteServiceRepository.fetchOrLoadConfig(application.getAppContext())
return when (configResult) {
......@@ -296,6 +306,11 @@ class RobertManagerImpl(
}?.value as? Number)?.let { quarantinePeriod ->
keystoreRepository.quarantinePeriod = quarantinePeriod.toInt()
}
(configuration?.firstOrNull {
it.name == RobertConstant.CONFIG.MIN_STATUS_RETRY_DURATION
}?.value as? Number)?.let { minStatusRetryDuration ->
keystoreRepository.minStatusRetryDuration = minStatusRetryDuration.toFloat()
}
(configuration?.firstOrNull {
it.name == RobertConstant.CONFIG.CHECK_STATUS_FREQUENCY
}?.value as? Number)?.let { checkStatusFrequency ->
......@@ -333,13 +348,13 @@ class RobertManagerImpl(
}
(configuration?.firstOrNull {
it.name == RobertConstant.CONFIG.QR_CODE_DELETION_HOURS
}?.value as? Float)?.let { deletionHours ->
keystoreRepository.qrCodeDeletionHours = deletionHours
}?.value as? Number)?.let { deletionHours ->
keystoreRepository.qrCodeDeletionHours = deletionHours.toFloat()
}
(configuration?.firstOrNull {
it.name == RobertConstant.CONFIG.QR_CODE_EXPIRED_HOURS
}?.value as? Float)?.let { expiredHours ->
keystoreRepository.qrCodeExpiredHours = expiredHours
}?.value as? Number)?.let { expiredHours ->
keystoreRepository.qrCodeExpiredHours = expiredHours.toFloat()
}
(configuration?.firstOrNull {
it.name == RobertConstant.CONFIG.QR_CODE_FORMATTED_STRING
......@@ -361,6 +376,13 @@ class RobertManagerImpl(
}?.value as? Boolean)?.let { displayDepartmentLevel ->
keystoreRepository.displayDepartmentLevel = displayDepartmentLevel
}
(configuration?.firstOrNull {
it.name == RobertConstant.CONFIG.PROXIMITY_REACTICATION_REMINDER_HOURS
}?.value as? List<*>)?.let { proximityReactivateReminderHours ->
val gson = Gson()
val typeToken = object : TypeToken<List<Int>>() {}.type
keystoreRepository.proximityReactivationReminderHours = gson.fromJson(gson.toJson(proximityReactivateReminderHours), typeToken)
}
}
override suspend fun activateProximity(application: RobertApplication, statusTried: Boolean): RobertResult {
......@@ -392,13 +414,28 @@ class RobertManagerImpl(
application.refreshProximityService()
}
@OptIn(ExperimentalTime::class)
override suspend fun updateStatus(robertApplication: RobertApplication): RobertResult {
return withContext(Dispatchers.IO) {
refreshConfig(robertApplication)
statusSemaphore.withPermit {
val ssu = getSSU(RobertConstant.PREFIX.C2)
if (abs((atRiskLastRefresh ?: 0L) - System.currentTimeMillis()) > RobertConstant.MIN_GAP_SUCCESS_STATUS) {
val checkStatusFrequencyHourMs = Duration.convert(
checkStatusFrequencyHour.toDouble(),
DurationUnit.HOURS,
DurationUnit.MILLISECONDS
)
val lastSuccessLongEnough =
abs((atRiskLastRefresh ?: 0L) - System.currentTimeMillis()) > checkStatusFrequencyHourMs
val minStatusRetryDurationMs = Duration.convert(
minStatusRetryDuration.toDouble(),
DurationUnit.HOURS,
DurationUnit.MILLISECONDS
)
val lastErrorLongEnough =
abs((keystoreRepository.atRiskLastError ?: 0L) - System.currentTimeMillis()) > minStatusRetryDurationMs
val shouldRefreshStatus = lastSuccessLongEnough || lastErrorLongEnough
if (shouldRefreshStatus) {
if (ssu is RobertResultData.Success) {
val result = remoteServiceRepository.status(apiVersion, ssu.data)
when (result) {
......@@ -426,6 +463,7 @@ class RobertManagerImpl(
RobertResult.Success()
} catch (e: Exception) {
keystoreRepository.atRiskLastError = System.currentTimeMillis()
when (e) {
is RobertException -> {
RobertResult.Failure(e)
......@@ -437,6 +475,7 @@ class RobertManagerImpl(
}
}
is RobertResultData.Failure -> {
keystoreRepository.atRiskLastError = System.currentTimeMillis()
RobertResult.Failure(result.error)
}
}
......@@ -465,11 +504,23 @@ class RobertManagerImpl(
localProximityRepository.removeUntilTime(localProximityExpiredTime)
}
override suspend fun report(token: String, firstSymptoms: Int, application: RobertApplication): RobertResult {
val preSymptomsSpan = (keystoreRepository.preSymptomsSpan ?: RobertConstant.PRE_SYMPTOMS_SPAN).toLong()
val firstProximityToSendTime =
(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(firstSymptoms.toLong()) - TimeUnit.DAYS.toMillis(preSymptomsSpan))
.unixTimeMsToNtpTimeS()
override suspend fun report(token: String, firstSymptoms: Int?, positiveTest: Int?, application: RobertApplication): RobertResult {
// Max take hello 14 days from now
var originDayInPast = (keystoreRepository.dataRetentionPeriod ?: RobertConstant.DATA_RETENTION_PERIOD).toLong()
when {
firstSymptoms != null -> {
// if symptoms take `preSymptomsSpan` days before first symptoms
val preSymptomsSpan = (keystoreRepository.preSymptomsSpan ?: RobertConstant.PRE_SYMPTOMS_SPAN).toLong()
originDayInPast = min(originDayInPast, firstSymptoms.toLong() + preSymptomsSpan)
}
positiveTest != null -> {
// if positive test take `positiveSampleSpan` days before positive test
val positiveSampleSpan = (keystoreRepository.positiveSampleSpan ?: RobertConstant.POSITIVE_SAMPLE_SPAN).toLong()
originDayInPast = min(originDayInPast, positiveTest.toLong() + positiveSampleSpan)
}
}
val firstProximityToSendTime = (System.currentTimeMillis() - TimeUnit.DAYS.toMillis(originDayInPast))
.unixTimeMsToNtpTimeS()