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

Update to 2.6.0

- Remove empty desc sent to analytics server
- Fix crash when reporting
- Vaccination certificate
parent 7f8ea789
......@@ -11,6 +11,7 @@
package com.lunabeestudio.analytics.extension
import com.lunabeestudio.analytics.model.TimestampedEvent
import com.lunabeestudio.analytics.network.model.TimestampedEventRQ
import com.lunabeestudio.analytics.proto.ProtoStorage
fun TimestampedEvent.toProto(): ProtoStorage.TimestampedEventProto {
......@@ -31,6 +32,20 @@ fun List<TimestampedEvent>.toProto(): ProtoStorage.TimestampedEventProtoList {
return builder.build()
}
private fun TimestampedEvent.toAPI(): TimestampedEventRQ {
return TimestampedEventRQ(
name = name,
timestamp = timestamp,
desc = desc.takeIf { it.isNotBlank() }
)
}
fun List<TimestampedEvent>.toAPI(): List<TimestampedEventRQ> {
return this.map { timestampedEvent ->
timestampedEvent.toAPI()
}
}
private fun ProtoStorage.TimestampedEventProto.toDomain(): TimestampedEvent =
TimestampedEvent(name, timestamp, desc)
......
......@@ -16,6 +16,7 @@ import android.os.Build
import androidx.core.content.edit
import androidx.core.util.AtomicFile
import androidx.lifecycle.LifecycleObserver
import com.lunabeestudio.analytics.extension.toAPI
import com.lunabeestudio.analytics.extension.toDomain
import com.lunabeestudio.analytics.extension.toProto
import com.lunabeestudio.analytics.model.AnalyticsResult
......@@ -145,8 +146,8 @@ object AnalyticsManager : LifecycleObserver {
val sendAnalyticsRQ = SendAnalyticsRQ(
installationUuid = sharedPreferences.getString(SHARED_PREFS_INSTALLATION_UUID, null) ?: UUID.randomUUID().toString(),
infos = appInfos,
events = appEvents,
errors = appErrors
events = appEvents.toAPI(),
errors = appErrors.toAPI()
)
withContext(Dispatchers.IO) {
val result = AnalyticsServerManager.sendAnalytics(
......@@ -185,7 +186,7 @@ object AnalyticsManager : LifecycleObserver {
val sendAnalyticsRQ = SendAnalyticsRQ(
installationUuid = UUID.randomUUID().toString(),
infos = healthInfos,
events = healthEvents,
events = healthEvents.toAPI(),
errors = emptyList()
)
withContext(Dispatchers.IO) {
......@@ -358,4 +359,4 @@ object AnalyticsManager : LifecycleObserver {
private fun <T> executeActionOnAtomicFile(action: () -> T): T {
return action()
}
}
\ No newline at end of file
}
......@@ -16,6 +16,6 @@ import com.lunabeestudio.analytics.model.TimestampedEvent
class SendAnalyticsRQ(
val installationUuid: String,
val infos: Infos,
val events: List<TimestampedEvent>,
val errors: List<TimestampedEvent>
val events: List<TimestampedEventRQ>,
val errors: List<TimestampedEventRQ>
)
\ 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/05/10 - for the TOUS-ANTI-COVID project
*/
package com.lunabeestudio.analytics.network.model
import com.lunabeestudio.analytics.model.Infos
import com.lunabeestudio.analytics.model.TimestampedEvent
class TimestampedEventRQ(
var name: String,
var timestamp: String,
var desc: String?,
)
\ No newline at end of file
......@@ -42,7 +42,7 @@
<dimen name="button_letter_spacing">0</dimen>
<dimen name="badge_size">8dp</dimen>
<dimen name="qr_code_size">200dp</dimen>
<dimen name="qr_code_fullscreen_size">300dp</dimen>
<dimen name="qr_code_fullscreen_size">240dp</dimen>
<dimen name="loading_elevation">40dp</dimen>
<dimen name="recycler_tag_padding_start">12dp</dimen>
......
......@@ -39,6 +39,8 @@
<item name="dividerColor">@color/color_mercury</item>
</style>
<style name="Theme.Base.StopCovid.ForceLight" parent="Theme.MaterialComponents.Light" />
<!-- Base application theme. -->
<style name="Theme.StopCovid" parent="Theme.Base.StopCovid" />
......
......@@ -15,8 +15,10 @@ import com.lunabeestudio.domain.model.WalletCertificateType
import java.util.Locale
import java.util.concurrent.TimeUnit
fun Configuration.walletOldCertificateThresholdInMs(type: WalletCertificateType): Long {
return TimeUnit.DAYS.toMillis((walletOldCertificateThresholdInDays[type.code.toLowerCase(Locale.getDefault())])?.toLong() ?: 0.toLong())
fun Configuration.walletOldCertificateThresholdInMs(type: WalletCertificateType): Long? {
return walletOldCertificateThresholdInDays[type.code.toLowerCase(Locale.getDefault())]?.let {
TimeUnit.DAYS.toMillis(it.toLong())
}
}
fun Configuration.walletPublicKey(authority: String, certificateId: String): String? {
......
......@@ -3,8 +3,49 @@ package com.lunabeestudio.domain.model
enum class WalletCertificateType {
SANITARY {
override val code: String = "B2"
override val validationRegexp: Regex = "^[A-Z\\d]{4}([A-Z\\d]{4})([A-Z\\d]{4})[A-Z\\d]{8}B2([A-Z\\d]{4})F0([A-Z\\d\\s\\/]+)\\x1D?F1([A-Z\\s]+)\\x1D?F2(\\d{8})F3([FMU]{1})F4([A-Z\\d]{3,7})\\x1D?F5([PNIX]{1})F6(\\d{12})\\x1F{1}([A-Z\\d]{103})$".toRegex()
override val validationRegexp: Regex = "^[A-Z\\d]{4}" // Characters 0 to 3 are ignored. They represent the document format version.
.plus("([A-Z\\d]{4})") // 1 - Characters 4 to 7 represent the document signing authority.
.plus("([A-Z\\d]{4})") // 2 - Characters 8 to 11 represent the id of the certificate used to sign the document.
.plus("[A-Z\\d]{8}") // Characters 12 to 19 are ignored.
.plus("B2") // Characters 20 and 21 represent the wallet certificate type (sanitary, ...)
.plus("[A-Z\\d]{4}") // Characters 22 to 25 are ignored.
.plus("F0([^\\x1D]+)\\x1D?") // 3 - We capture the field F0. It must have at least one character.
.plus("F1([^\\x1D]+)\\x1D?") // 4 - We capture the field F1. It must have at least one character.
.plus("F2(\\d{8})") // 5 - We capture the field F2. It can only contain digits.
.plus("F3([FMU]{1})") // 6 - We capture the field F3. It can only contain "F", "M" or "U".
.plus("F4([A-Z\\d]{3,7})\\x1D?") // 7 - We capture the field F4. It can contain 3 to 7 uppercase letters and/or digits. It can also be ended by the GS ASCII char (29) if the field reaches its max length.
.plus("F5([PNIX]{1})") // 8 - We capture the field F5. It can only contain "P", "N", "I" or "X".
.plus("F6(\\d{12})") // 9 - We capture the field F6. It can only contain digits.
.plus("\\x1F{1}") // This character is separating the message from its signature.
.plus("([A-Z\\d\\=]+)$").toRegex() // 10 - This is the message signature.
override val stringKey: String = "sanitaryCertificate"
},
VACCINATION {
override val code: String = "L1"
override val validationRegexp: Regex = "^[A-Z\\d]{4}" // Characters 0 to 3 are ignored. They represent the document format version.
.plus("([A-Z\\d]{4})") // 1 - Characters 4 to 7 represent the document signing authority.
.plus("([A-Z\\d]{4})") // 2 - Characters 8 to 11 represent the id of the certificate used to sign the document.
.plus("[A-Z\\d]{8}") // Characters 12 to 19 are ignored.
.plus("L1") // Characters 20 and 21 represent the wallet certificate type (sanitary, ...)
.plus("[A-Z\\d]{4}") // Characters 22 to 25 are ignored.
.plus("L0([^\\x1D]+)\\x1D?") // 3 - We capture the field L0. It can contain uppercased letters and spaces. It can also be ended by the GS ASCII char (29) if the field reaches its max length.
.plus("L1([^\\x1D]+)\\x1D?") // 4 - We capture the field L1. It must have at least one character.
.plus("L2(\\d{8})\\x1D?") // 5 - We capture the field L2. It can only contain 8 digits.
.plus("L3([^\\x1D]*)\\x1D?") // // 6 - We capture the field L3. It can contain any characters.
.plus("L4([^\\x1D]+)\\x1D?") // 7 - We capture the field L4. It must have at least one character
.plus("L5([^\\x1D]+)\\x1D?") // 8 - We capture the field L5. It must have at least one character
.plus("L6([^\\x1D]+)\\x1D?") // 9 - We capture the field L6. It must have at least one character
.plus("L7(\\d{1})") // 10 - We capture the field L7. It can contain only one digit.
.plus("L8(\\d{1})") // 11 - We capture the field L8. It can contain only one digit.
.plus("L9(\\d{8})") // 12 - We capture the field L9. It can only contain 8 digits.
.plus("LA([A-Z\\d]{2})") // 13 - We capture the field LA. 2 characters letters or digits
.plus("\\x1F{1}") // This character is separating the message from its signature.
.plus("([A-Z\\d\\=]+)$").toRegex() // 14 - This is the message signature.
override val stringKey: String = "vaccinationCertificate"
};
abstract val code: String
......
......@@ -10,6 +10,7 @@
package com.lunabeestudio.framework.local.datasource
import androidx.core.util.AtomicFile
import com.lunabeestudio.domain.extension.unixTimeMsToNtpTimeS
import com.lunabeestudio.domain.model.LocalProximity
import com.lunabeestudio.framework.extension.toDomain
......@@ -68,8 +69,13 @@ open class SecureFileLocalProximityDataSource(
}?.flatMap {
it.listFiles()?.asList() ?: emptyList()
}?.flatMap {
cryptoManager.createCipherInputStream(it.inputStream()).use { cis ->
ProtoStorage.LocalProximityProtoList.parseFrom(cis).toDomain()
try {
cryptoManager.createCipherInputStream(it.inputStream()).use { cis ->
ProtoStorage.LocalProximityProtoList.parseFrom(cis).toDomain()
}
} catch (e: Exception) {
Timber.e(e)
emptyList()
}
} ?: emptyList()
}
......@@ -132,8 +138,9 @@ open class SecureFileLocalProximityDataSource(
var lastDumpedIndex: Int
var dumpTime = System.currentTimeMillis()
val tmpFile = createTempFile(directory = encryptedFile.parentFile)
cryptoManager.createCipherOutputStream(tmpFile.outputStream()).use { cos ->
val atomicFile = AtomicFile(encryptedFile)
val fileOutputStream = atomicFile.startWrite()
cryptoManager.createCipherOutputStream(fileOutputStream).use { cos ->
val proto = cacheMtx.withLock {
Timber.v("Start dumping ${localProximityList.size} items to ${encryptedFile.absolutePath}")
lastDumpedIndex = (localProximityList.size - 1).coerceAtLeast(0)
......@@ -141,7 +148,7 @@ open class SecureFileLocalProximityDataSource(
}
proto.writeTo(cos)
}
tmpFile.renameTo(encryptedFile)
atomicFile.finishWrite(fileOutputStream)
dumpTime = System.currentTimeMillis() - dumpTime
Timber.v("Dumping cache to ${encryptedFile.absolutePath} done in ${dumpTime}ms")
......
......@@ -43,8 +43,8 @@ android {
applicationId "fr.gouv.android.stopcovid"
minSdkVersion 21
targetSdkVersion 30
versionCode 210
versionName "2.5.0"
versionCode 214
versionName "2.6.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
......
......@@ -19,6 +19,7 @@ import com.lunabeestudio.robert.RobertApplication
import com.lunabeestudio.stopcovid.extension.isOld
import com.lunabeestudio.stopcovid.extension.isRecent
import com.lunabeestudio.stopcovid.model.SanitaryCertificate
import com.lunabeestudio.stopcovid.model.VaccinationCertificate
import com.lunabeestudio.stopcovid.model.WalletCertificateMalformedException
import com.lunabeestudio.support.extension.doNotCheckCertificatesKey
import com.lunabeestudio.support.robert.SupportRobertManager
......@@ -44,9 +45,11 @@ class WalletManagerTest {
WalletManager.verifyCertificateCodeValue(sharedPreferences, configuration, "")
}
assertThrows<WalletCertificateMalformedException> {
WalletManager.verifyCertificateCodeValue(sharedPreferences,
WalletManager.verifyCertificateCodeValue(
sharedPreferences,
configuration,
WalletManager.extractCertificateCodeFromUrl("https://bonjour.tousanticovid.gouv.fr/app/wallet?v="))
WalletManager.extractCertificateCodeFromUrl("https://bonjour.tousanticovid.gouv.fr/app/wallet?v=")
)
}
assertThrows<WalletCertificateMalformedException> {
WalletManager.verifyCertificateCodeValue(
......@@ -75,11 +78,13 @@ class WalletManagerTest {
var walletCertificate = WalletManager.verifyCertificateCodeValue(
sharedPreferences,
configuration,
WalletManager.extractCertificateCodeFromUrl("https://bonjour.tousanticovid.gouv.fr/app/wallet?v=DC04DHI0TST11E3C1E3CB201FRF0JEAN%20LOUIS/EDOUARD%1DF1DUPOND%1DF225111980F3MF494309%1DF5NF6${
dateFormat.format(
oldDate
)
}%1FZCQ5EDEXRCRYMU4U5U4YQSF5GOE2PMFFC6PDWOMZK64434TUCJWQLIXCRYMA5TWVT7TEZSF2S3ZCJSYK3JYFOBVUHNOEXQMEKWQDG3A")
WalletManager.extractCertificateCodeFromUrl(
"https://bonjour.tousanticovid.gouv.fr/app/wallet?v=DC04DHI0TST11E3C1E3CB201FRF0JEAN%20LOUIS/EDOUARD%1DF1DUPOND%1DF225111980F3MF494309%1DF5NF6${
dateFormat.format(
oldDate
)
}%1FZCQ5EDEXRCRYMU4U5U4YQSF5GOE2PMFFC6PDWOMZK64434TUCJWQLIXCRYMA5TWVT7TEZSF2S3ZCJSYK3JYFOBVUHNOEXQMEKWQDG3A"
)
)
assert(walletCertificate is SanitaryCertificate)
assert(walletCertificate.keyAuthority == "DHI0")
......@@ -96,7 +101,7 @@ class WalletManagerTest {
)
assert((walletCertificate as SanitaryCertificate).firstName == "JEAN LOUIS, EDOUARD")
assert(walletCertificate.name == "DUPOND")
assert(walletCertificate.birthDate?.time == 343954800000L)
assert(walletCertificate.birthDate == "25-11-1980")
assert(walletCertificate.gender == "M")
assert(walletCertificate.testResult == "N")
assert(walletCertificate.analysisDate == dateFormat.parse(dateFormat.format(oldDate))!!.time)
......@@ -108,11 +113,13 @@ class WalletManagerTest {
walletCertificate = WalletManager.verifyCertificateCodeValue(
sharedPreferences,
configuration,
WalletManager.extractCertificateCodeFromUrl("https://bonjour.tousanticovid.gouv.fr/app/wallet?v=DC04DHI0TST11E3C1E3CB201FRF0JEAN%20LOUIS/EDOUARD%1DF1DUPOND%1DF225111980F3FF494309%1DF5NF6${
dateFormat.format(
recentDate
)
}%1FZCQ5EDEXRCRYMU4U5U4YQSF5GOE2PMFFC6PDWOMZK64434TUCJWQLIXCRYMA5TWVT7TEZSF2S3ZCJSYK3JYFOBVUHNOEXQMEKWQDG3A")
WalletManager.extractCertificateCodeFromUrl(
"https://bonjour.tousanticovid.gouv.fr/app/wallet?v=DC04DHI0TST11E3C1E3CB201FRF0JEAN%20LOUIS/EDOUARD%1DF1DUPOND%1DF225111980F3FF494309%1DF5NF6${
dateFormat.format(
recentDate
)
}%1FZCQ5EDEXRCRYMU4U5U4YQSF5GOE2PMFFC6PDWOMZK64434TUCJWQLIXCRYMA5TWVT7TEZSF2S3ZCJSYK3JYFOBVUHNOEXQMEKWQDG3A"
)
)
assert(walletCertificate is SanitaryCertificate)
assert(walletCertificate.keyAuthority == "DHI0")
......@@ -129,13 +136,52 @@ class WalletManagerTest {
)
assert((walletCertificate as SanitaryCertificate).firstName == "JEAN LOUIS, EDOUARD")
assert(walletCertificate.name == "DUPOND")
assert(walletCertificate.birthDate?.time == 343954800000L)
assert(walletCertificate.birthDate == "25-11-1980")
assert(walletCertificate.gender == "F")
assert(walletCertificate.testResult == "N")
assert(walletCertificate.analysisDate == dateFormat.parse(dateFormat.format(recentDate))!!.time)
assert(walletCertificate.analysisCode == "94309")
assert(walletCertificate.isRecent(context.robertManager().configuration))
assert(!walletCertificate.isOld(context.robertManager().configuration))
val vaccinationDateFormat = SimpleDateFormat("ddMMyyyy", Locale.US)
walletCertificate = WalletManager.verifyCertificateCodeValue(
sharedPreferences,
configuration,
WalletManager.extractCertificateCodeFromUrl(
"https://bonjour.tousanticovid.gouv.fr/app/wallet?v=DC04FR0000011E671E67L101FRL0THEOULE SUR MER\u001DL1JEAN PAUL\u001DL231051962L3COVID-19\u001DL4J07BX03\u001DL5COMIRNATY PFIZER/BIONTECH\u001DL6COMIRNATY PFIZER/BIONTECH\u001DL71L82L9${
vaccinationDateFormat.format(
recentDate
)
}LACO\u001FS27NCTCO3RXXKLJBIPXZGQSMW4SJIP45RO45IHCJAY4RESQZCQHX46USBZ75F5JQG7MQ3Q5PRFNUHSMTVR23L7EL4H5YAKAYSL4ANIA"
)
)
assert(walletCertificate is VaccinationCertificate)
assert(walletCertificate.keyAuthority == "FR00")
assert(walletCertificate.keyCertificateId == "0001")
assert(walletCertificate.keySignature == "")
assert(walletCertificate.timestamp == vaccinationDateFormat.parse(vaccinationDateFormat.format(recentDate))!!.time)
assert(walletCertificate.type == WalletCertificateType.VACCINATION)
assert(
walletCertificate.value == "DC04FR0000011E671E67L101FRL0THEOULE SUR MER\u001DL1JEAN PAUL\u001DL231051962L3COVID-19\u001DL4J07BX03\u001DL5COMIRNATY PFIZER/BIONTECH\u001DL6COMIRNATY PFIZER/BIONTECH\u001DL71L82L9${
vaccinationDateFormat.format(
recentDate
)
}LACO\u001FS27NCTCO3RXXKLJBIPXZGQSMW4SJIP45RO45IHCJAY4RESQZCQHX46USBZ75F5JQG7MQ3Q5PRFNUHSMTVR23L7EL4H5YAKAYSL4ANIA"
)
assert((walletCertificate as VaccinationCertificate).firstName == "JEAN PAUL")
assert(walletCertificate.name == "THEOULE SUR MER")
assert(walletCertificate.birthDate == "31-05-1962")
assert(walletCertificate.diseaseName == "COVID-19")
assert(walletCertificate.prophylacticAgent == "J07BX03")
assert(walletCertificate.vaccineName == "COMIRNATY PFIZER/BIONTECH")
assert(walletCertificate.vaccineMaker == "COMIRNATY PFIZER/BIONTECH")
assert(walletCertificate.lastVaccinationStateRank == "1")
assert(walletCertificate.completeCycleDosesCount == "2")
assert(walletCertificate.lastVaccinationDate == vaccinationDateFormat.parse(vaccinationDateFormat.format(recentDate)))
assert(walletCertificate.vaccinationCycleState == "CO")
assert(walletCertificate.isRecent(context.robertManager().configuration))
assert(!walletCertificate.isOld(context.robertManager().configuration))
}
private inline fun <reified T : Exception> assertThrows(runnable: () -> Any?) {
......
......@@ -2,11 +2,11 @@
"config": [
{
"name": "lastUpdate",
"value": "12 april 2021"
"value": "23 april 2021"
},
{
"name": "version",
"value": 44
"value": 47
},
{
"name": "versionCalibrationBle",
......@@ -70,7 +70,7 @@
},
{
"name": "app.isAnalyticsOn",
"value": true
"value": false
},
{
"name": "app.wallet.oldCertificateThresholdInDays",
......@@ -85,24 +85,14 @@
{
"name": "app.walletPubKeys",
"value": [
{
"auth": "DHI0",
"pubKeys": {
"TST1": "-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6lPsrLPPis5Wl9u8Y7THfNIIfeEyq3q72MpzUm3gvr6ctYk4d3OQhn76GKSjSYX/0ZRC/cYO9479K0SUzYZ9yg==-----END PUBLIC KEY-----"
}
},
{
"auth": "FR00",
"pubKeys": {
"0001": "-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqY8NfM1igIiTvsTUNuedGDSh1uAB1w8cTNzNnZ4v4in3JAUU6N3AypjQx0QMnMSShJoPvac/w5L02grgf4TCPA==-----END PUBLIC KEY-----"
}
},
{
"auth": "FR03",
"pubKeys":
{
"AHP1": "-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPnxJntwNwme9uHSasmGFFwdC0FWNEpucgzhjr+/AZ6UuTm3kL3ogEUAwKU0tShEVmZNK4/lM05h+0ZvtboJM/A==-----END PUBLIC KEY-----",
"AHP2": "-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOYUgmx8pKu0UbyqQ/kt4+PXSpUprkO2YLHmzzoN66XjDW0AnSzXorFPe556p73Vawqaoy3qQKDIDB62IBYWBuA==-----END PUBLIC KEY-----"
"AHP2": "-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOYUgmx8pKu0UbyqQ/kt4+PXSpUprkO2YLHmzzoN66XjDW0AnSzXorFPe556p73Vawqaoy3qQKDIDB62IBYWBuA==-----END PUBLIC KEY-----",
"AV01": "-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1T9uG2bEP7uWND6RT/lJs2y787BkEJoRMMLXvqPKFFC3ckqFAPnFjbiv/odlWH04a1P9CvaCRxG31FMEOFZyXA==-----END PUBLIC KEY-----",
"AV02": "-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3jL6zQ0aQj9eJHUw4VDHB9sMoviLVIlADnoBwC43Md8p9w655z2bDhYEEajQ2amQzt+eU7HdWrvqY23Do91Izg==-----END PUBLIC KEY-----"
}
}
]
......
......@@ -208,7 +208,7 @@
"onboarding.noBleController.mainMessage.subtitle": "But you can still help in the fight against COVID-19 by sharing this app. The more people use it, the more effective it will be.\nTell your friends and family!",
"onboarding.noBleController.accept": "Share the app",
"onboarding.noBleController.infos": "Compatible smartphones",
"onboarding.noBleController.infosUrl": "https://app.tousanticovid.gouv.fr/json/version-29/devices.html",
"onboarding.noBleController.infosUrl": "https://bonjour.tousanticovid.gouv.fr/devices.html",
"common.error.unauthorized": "You don't have the authorisation needed to do this.",
"common.error.unknown": "An error occurred.",
"common.error.proximityUnknown": "A Bluetooth error occurred.",
......@@ -401,7 +401,7 @@
"onboarding.runWithoutBleController.mainMessage.title": "TousAntiCovid will not be able to detect your \"Bluetooth contacts\"",
"onboarding.runWithoutBleController.mainMessage.subtitle": "Unfortunately, your phone is not fully compatible with Bluetooth Low Energy, which is used to detect your \"Bluetooth contacts\". However, you can still access the other functions of the app.",
"onboarding.runWithoutBleController.infos": "Compatible phones",
"onboarding.runWithoutBleController.infosUrl": "https://app.tousanticovid.gouv.fr/json/version-29/devices.html",
"onboarding.runWithoutBleController.infosUrl": "https://bonjour.tousanticovid.gouv.fr/devices.html",
"keyFigures.update": "Data from: %@",
"declareController.codeNotReceived.buttonTitle": "I need help",
"declareController.codeNotReceived.alert.title": "I need help",
......@@ -533,7 +533,7 @@
"home.healthSection.isSick.title": "Take care",
"home.healthSection.isSick.subtitle": "Find out more",
"myHealthController.sick.title": "Health recommendations",
"home.attestationSection.title": "Form",
"home.attestationSection.title": "Form & Wallet",
"home.attestationSection.cell.title":"Your forms",
"home.attestationSection.cell.subtitle.noAttestations": "Tap here",
"home.attestationSection.cell.subtitle.oneAttestation": "1 form",
......@@ -560,7 +560,7 @@
"sendHistoryController.successAlert.message": "Thank you for identifying yourself via the TousAntiCovid app and for informing those users you were in close contact with while you were contagious.\n\nThanks to you, these users will be able to take the necessary precautions in order to protect themselves and others. Let's keep up the fight against Covid.",
"sendHistoryController.successAlert.button.learnMore": "Find out more",
"keyFiguresExplanationsController.title": "Explanation of figures",
"keyFiguresController.section.health.subtitle": "⚠️ A partir d’aujourd’hui, la semaine glissante d’étude n’inclut pas le lundi de pâques, et sa comparaison avec les 7 jours précédents, qui inclut le lundi de pâques, est à faire avec prudence et n’est pas à considérer comme une hausse exceptionnelle du nombre de cas.\n\nTap \"read now\" to find out more about the source of the health data, how they are interpreted and updated.",
"keyFiguresController.section.health.subtitle": "Tap \"read now\" to find out more about the source of the health data, how they are interpreted and updated.",
"keyFiguresController.section.health.button": "Read now",
"home.activation.sick.alert.title": "Information",
"home.activation.sick.alert.message": "The alert for exposure to COVID-19 cannot be activated for 2 months after you declare yourself positive for COVID-19.\n\nFor 2 months after testing positive for COVID-19, you will not be considered as being \"at-risk of infection\".",
......@@ -963,9 +963,9 @@
"walletController.title": "Test wallet",
"walletController.explanations.title": "How does it work?",
"walletController.explanations.subtitle": "To digitalize your test certificates and always have them at hand, it's simple: once the result of your test is available, you will receive a text message with a link and instructions to follow.\n\nThis certificate will have to be presented during checks for your air travels 👉 experiment in progress, only on some flights to Corsica (Air France and Air Corsica will inform their eligible customers).\n\nIf you have a problem, you can call 0 800 08 71 48.",
"walletController.explanations.subtitle": "To digitalize your test certificates and always have them at hand, it's simple: once the result of your test is available, you will receive a text message with a link (from SI-DEP platform) and instructions to follow.\n\nThis certificate will have to be presented during checks for your air travels 👉 experiment in progress, only on some flights to Corsica (Air France and Air Corsica will inform their eligible customers).\n\nIf you have a problem, you can call 0 800 08 71 48.",
"walletController.flashButton.title": "Scan QR Code",
"walletController.flashExplanation": "On your your SIDEP document with your test results, you have a QR Code that you can scan to add your certificate in this wallet.",
"walletController.flashExplanation": "On your SIDEP document with your test results, you have a QR Code that you can scan to add your certificate in this wallet.",
"walletController.recentCertificatesSection.title": "Recent certificates",
"walletController.recentCertificatesSection.subtitle": "These certificates are less than 3 days old.",
"walletController.oldCertificatesSection.title": "Certificates older than 3 days",
......@@ -1054,6 +1054,12 @@
"wallet.proof.error.2.message": "The authenticity of the scanned certificate could not be verified. If the problem persists, please call 0 800 08 71 48",
"wallet.proof.moreThanSpecificHours": "⚠️ More than %dh",
"wallet.proof.lessThanSpecificHours": "Less than %dh"
"wallet.proof.lessThanSpecificHours": "Less than %dh",
"wallet.proof.vaccinationCertificate.LA.CO": "On-going",
"wallet.proof.vaccinationCertificate.LA.EC": "On-going",
"wallet.proof.vaccinationCertificate.LA.TE": "Completed",
"wallet.proof.vaccinationCertificate.pillTitle": "Vaccine",
"wallet.proof.vaccinationCertificate.description": "<L1>, <L0>\nDate of birth: <L2>\n<L5> (<L4>)\nInjection date <L9>\nDose: <L7>/<L8>"
}
......@@ -208,7 +208,7 @@
"onboarding.noBleController.mainMessage.subtitle": "But you can still help in the fight against COVID-19 by sharing this app. The more people use it, the more effective it will be.\nTell your friends and family!",
"onboarding.noBleController.accept": "Share the app",
"onboarding.noBleController.infos": "Compatible smartphones",
"onboarding.noBleController.infosUrl": "https://app.tousanticovid.gouv.fr/json/version-29/devices.html",
"onboarding.noBleController.infosUrl": "https://bonjour.tousanticovid.gouv.fr/devices.html",
"common.error.unauthorized": "You don't have the authorisation needed to do this.",
"common.error.unknown": "An error occurred.",
"common.error.proximityUnknown": "A Bluetooth error occurred.",
......@@ -401,7 +401,7 @@
"onboarding.runWithoutBleController.mainMessage.title": "TousAntiCovid will not be able to detect your \"Bluetooth contacts\"",
"onboarding.runWithoutBleController.mainMessage.subtitle": "Unfortunately, your phone is not fully compatible with Bluetooth Low Energy, which is used to detect your \"Bluetooth contacts\". However, you can still access the other functions of the app.",
"onboarding.runWithoutBleController.infos": "Compatible phones",
"onboarding.runWithoutBleController.infosUrl": "https://app.tousanticovid.gouv.fr/json/version-29/devices.html",
"onboarding.runWithoutBleController.infosUrl": "https://bonjour.tousanticovid.gouv.fr/devices.html",
"keyFigures.update": "Data from: %@",
"declareController.codeNotReceived.buttonTitle": "I need help",
"declareController.codeNotReceived.alert.title": "I need help",
......@@ -533,7 +533,7 @@
"home.healthSection.isSick.title": "Take care",
"home.healthSection.isSick.subtitle": "Find out more",
"myHealthController.sick.title": "Health recommendations",
"home.attestationSection.title": "Form",
"home.attestationSection.title": "Form & Wallet",
"home.attestationSection.cell.title":"Your forms",
"home.attestationSection.cell.subtitle.noAttestations": "Tap here",
"home.attestationSection.cell.subtitle.oneAttestation": "1 form",
......@@ -560,7 +560,7 @@
"sendHistoryController.successAlert.message": "Thank you for identifying yourself via the TousAntiCovid app and for informing those users you were in close contact with while you were contagious.\n\nThanks to you, these users will be able to take the necessary precautions in order to protect themselves and others. Let's keep up the fight against Covid.",
"sendHistoryController.successAlert.button.learnMore": "Find out more",
"keyFiguresExplanationsController.title": "Explanation of figures",
"keyFiguresController.section.health.subtitle": "⚠️ A partir d’aujourd’hui, la semaine glissante d’étude n’inclut pas le lundi de pâques, et sa comparaison avec les 7 jours précédents, qui inclut le lundi de pâques, est à faire avec prudence et n’est pas à considérer comme une hausse exceptionnelle du nombre de cas.\n\nTap \"read now\" to find out more about the source of the health data, how they are interpreted and updated.",
"keyFiguresController.section.health.subtitle": "Tap \"read now\" to find out more about the source of the health data, how they are interpreted and updated.",
"keyFiguresController.section.health.button": "Read now",
"home.activation.sick.alert.title": "Information",
"home.activation.sick.alert.message": "The alert for exposure to COVID-19 cannot be activated for 2 months after you declare yourself positive for COVID-19.\n\nFor 2 months after testing positive for COVID-19, you will not be considered as being \"at-risk of infection\".",
......@@ -963,9 +963,9 @@
"walletController.title": "Test wallet",
"walletController.explanations.title": "How does it work?",
"walletController.explanations.subtitle": "To digitalize your test certificates and always have them at hand, it's simple: once the result of your test is available, you will receive a text message with a link and instructions to follow.\n\nThis certificate will have to be presented during checks for your air travels 👉 experiment in progress, only on some flights to Corsica (Air France and Air Corsica will inform their eligible customers).\n\nIf you have a problem, you can call 0 800 08 71 48.",
"walletController.explanations.subtitle": "To digitalize your test certificates and always have them at hand, it's simple: once the result of your test is available, you will receive a text message with a link (from SI-DEP platform) and instructions to follow.\n\nThis certificate will have to be presented during checks for your air travels 👉 experiment in progress, only on some flights to Corsica (Air France and Air Corsica will inform their eligible customers).\n\nIf you have a problem, you can call 0 800 08 71 48.",
"walletController.flashButton.title": "Scan QR Code",
"walletController.flashExplanation": "On your your SIDEP document with your test results, you have a QR Code that you can scan to add your certificate in this wallet.",
"walletController.flashExplanation": "On your SIDEP document with your test results, you have a QR Code that you can scan to add your certificate in this wallet.",
"walletController.recentCertificatesSection.title": "Recent certificates",
"walletController.recentCertificatesSection.subtitle": "These certificates are less than 3 days old.",
"walletController.oldCertificatesSection.title": "Certificates older than 3 days",
......@@ -1054,6 +1054,12 @@
"wallet.proof.error.2.message": "The authenticity of the scanned certificate could not be verified. If the problem persists, please call 0 800 08 71 48",
"wallet.proof.moreThanSpecificHours": "⚠️ More than %dh",
"wallet.proof.lessThanSpecificHours": "Less than %dh"
"wallet.proof.lessThanSpecificHours": "Less than %dh",
"wallet.proof.vaccinationCertificate.LA.CO": "On-going",
"wallet.proof.vaccinationCertificate.LA.EC": "On-going",
"wallet.proof.vaccinationCertificate.LA.TE": "Completed",
"wallet.proof.vaccinationCertificate.pillTitle": "Vaccine",
"wallet.proof.vaccinationCertificate.description": "<L1>, <L0>\nDate of birth: <L2>\n<L5> (<L4>)\nInjection date <L9>\nDose: <L7>/<L8>"
}
......@@ -208,7 +208,7 @@
"onboarding.noBleController.mainMessage.subtitle": "But you can still help in the fight against COVID-19 by sharing this app. The more people use it, the more effective it will be.\nTell your friends and family!",
"onboarding.noBleController.accept": "Share the app",
"onboarding.noBleController.infos": "Compatible smartphones",
"onboarding.noBleController.infosUrl": "https://app.tousanticovid.gouv.fr/json/version-29/devices.html",
"onboarding.noBleController.infosUrl": "https://bonjour.tousanticovid.gouv.fr/devices.html",
"common.error.unauthorized": "You don't have the authorisation needed to do this.",
"common.error.unknown": "An error occurred.",
"common.error.proximityUnknown": "A Bluetooth error occurred.",
......@@ -401,7 +401,7 @@
"onboarding.runWithoutBleController.mainMessage.title": "TousAntiCovid will not be able to detect your \"Bluetooth contacts\"",
"onboarding.runWithoutBleController.mainMessage.subtitle": "Unfortunately, your phone is not fully compatible with Bluetooth Low Energy, which is used to detect your \"Bluetooth contacts\". However, you can still access the other functions of the app.",
"onboarding.runWithoutBleController.infos": "Compatible phones",
"onboarding.runWithoutBleController.infosUrl": "https://app.tousanticovid.gouv.fr/json/version-29/devices.html",
"onboarding.runWithoutBleController.infosUrl": "https://bonjour.tousanticovid.gouv.fr/devices.html",
"keyFigures.update": "Data from: %@",
"declareController.codeNotReceived.buttonTitle": "I need help",
"declareController.codeNotReceived.alert.title": "I need help",
......@@ -533,7 +533,7 @@
"home.healthSection.isSick.title": "Take care",
"home.healthSection.isSick.subtitle": "Find out more",
"myHealthController.sick.title": "Health recommendations",
"home.attestationSection.title": "Form",
"home.attestationSection.title": "Form & Wallet",
"home.attestationSection.cell.title":"Your forms",
"home.attestationSection.cell.subtitle.noAttestations": "Tap here",
"home.attestationSection.cell.subtitle.oneAttestation": "1 form",
......@@ -560,7 +560,7 @@
"sendHistoryController.successAlert.message": "Thank you for identifying yourself via the TousAntiCovid app and for informing those users you were in close contact with while you were contagious.\n\nThanks to you, these users will be able to take the necessary precautions in order to protect themselves and others. Let's keep up the fight against Covid.",
"sendHistoryController.successAlert.button.learnMore": "Find out more",
"keyFiguresExplanationsController.title": "Explanation of figures",
"keyFiguresController.section.health.subtitle": "⚠️ A partir d’aujourd’hui, la semaine glissante d’étude n’inclut pas le lundi de pâques, et sa comparaison avec les 7 jours précédents, qui inclut le lundi de pâques, est à faire avec prudence et n’est pas à considérer comme une hausse exceptionnelle du nombre de cas.\n\nTap \"read now\" to find out more about the source of the health data, how they are interpreted and updated.",
"keyFiguresController.section.health.subtitle": "Tap \"read now\" to find out more about the source of the health data, how they are interpreted and updated.",
"keyFiguresController.section.health.button": "Read now",
"home.activation.sick.alert.title": "Information",
"home.activation.sick.alert.message": "The alert for exposure to COVID-19 cannot be activated for 2 months after you declare yourself positive for COVID-19.\n\nFor 2 months after testing positive for COVID-19, you will not be considered as being \"at-risk of infection\".",
......@@ -963,9 +963,9 @@
"walletController.title": "Test wallet",
"walletController.explanations.title": "How does it work?",