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

Update to 3.7.3

- Also send empty analytics event
- Warn when importing too many certificates
parent ca20dc8f
......@@ -194,38 +194,34 @@ object AnalyticsManager : LifecycleObserver {
token: String
) {
val healthEvents = getHealthEvents(context)
if (healthEvents.isNotEmpty()) {
val healthInfos = getHealthInfos(context, robertManager, analyticsInfosProvider)
val sendAnalyticsRQ = SendHealthAnalyticsRQ(
installationUuid = UUID.randomUUID().toString(),
infos = healthInfos,
events = healthEvents.toAPI(),
errors = emptyList()
val healthInfos = getHealthInfos(context, robertManager, analyticsInfosProvider)
val sendAnalyticsRQ = SendHealthAnalyticsRQ(
installationUuid = UUID.randomUUID().toString(),
infos = healthInfos,
events = healthEvents.toAPI(),
errors = emptyList()
)
withContext(Dispatchers.IO) {
val result = AnalyticsServerManager.sendAnalytics(
context,
analyticsInfosProvider.getBaseUrl(),
analyticsInfosProvider.getApiVersion(),
token,
sendAnalyticsRQ,
)
withContext(Dispatchers.IO) {
val result = AnalyticsServerManager.sendAnalytics(
context,
analyticsInfosProvider.getBaseUrl(),
analyticsInfosProvider.getApiVersion(),
token,
sendAnalyticsRQ,
)
when (result) {
is AnalyticsResult.Success -> {
withContext(Dispatchers.Main) {
resetHealthEvents(context)
}
when (result) {
is AnalyticsResult.Success -> {
withContext(Dispatchers.Main) {
resetHealthEvents(context)
}
is AnalyticsResult.Failure -> {
Timber.e(result.error)
if ((result.error as? HttpException)?.code() == 413) {
resetHealthEvents(context)
}
}
is AnalyticsResult.Failure -> {
Timber.e(result.error)
if ((result.error as? HttpException)?.code() == 413) {
resetHealthEvents(context)
}
}
}
} else {
Timber.d("No heath event to report")
}
}
......
......@@ -139,6 +139,8 @@ internal class ApiConfiguration(
val conversionPublicKey: String,
@SerializedName("app.wallet.conversionApiVersion")
val conversionApiVersion: Int,
@SerializedName("app.wallet.maxCertBeforeWarning")
val maxCertBeforeWarning: Int,
)
internal fun ApiConfiguration.toDomain(gson: Gson) = Configuration(
......@@ -223,4 +225,5 @@ internal fun ApiConfiguration.toDomain(gson: Gson) = Configuration(
certificateConversionSidepOnlyCode = gson.fromJson(certificateConversionSidepOnlyCode, object : TypeToken<List<String>>() {}.type),
conversionPublicKey = gson.fromJson(conversionPublicKey, object : TypeToken<Map<String, String>>() {}.type),
conversionApiVersion = conversionApiVersion,
maxCertBeforeWarning = maxCertBeforeWarning,
)
......@@ -45,9 +45,9 @@
<!-- Base application theme. -->
<style name="Theme.StopCovid.Colored" parent="Theme.Base.StopCovid">
<item name="colorPrimary">@color/color_black_33</item>
<item name="colorPrimary">@android:color/white</item>
<item name="colorOnPrimary">@android:color/white</item>
<item name="colorSurface">@color/color_black_33</item>
<item name="colorSurface">@android:color/white</item>
<item name="android:textColor">@android:color/white</item>
</style>
......
......@@ -34,6 +34,10 @@
<item name="android:textSize">@dimen/title_small_font_size</item>
</style>
<style name="TextAppearance.StopCovid.WarningText" parent="TextAppearance.AppCompat.Small">
<item name="fontFamily">@font/marianne</item>
</style>
<style name="TextAppearance.StopCovid.Error" parent="TextAppearance.MaterialComponents.Headline6">
<item name="android:textSize">@dimen/error_font_size</item>
<item name="android:textColor">@color/color_on_error</item>
......
......@@ -71,4 +71,5 @@ class Configuration(
var certificateConversionSidepOnlyCode: List<String>,
var conversionPublicKey: Map<String, String>,
var conversionApiVersion: Int,
var maxCertBeforeWarning: Int,
)
......@@ -96,7 +96,6 @@ class InGroupeDatasourceTest {
val inGroupeDatasource = InGroupeDatasource(
InstrumentationRegistry.getInstrumentation().context,
BouncyCastleCryptoDataSource(),
robertManager,
server.url("/").toString(),
)
......@@ -132,7 +131,6 @@ class InGroupeDatasourceTest {
val inGroupeDatasource = InGroupeDatasource(
InstrumentationRegistry.getInstrumentation().context,
BouncyCastleCryptoDataSource(),
robertManager,
server.url("/").toString(),
)
......
......@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
......@@ -9,7 +9,7 @@
*/
plugins {
id("de.fayard.refreshVersions") version "0.11.0"
id("de.fayard.refreshVersions") version "0.20.0"
}
rootProject.name = 'TousAntiCovid'
......
......@@ -45,8 +45,8 @@ android {
applicationId "fr.gouv.android.stopcovid"
minSdkVersion 21
targetSdkVersion 30
versionCode 332
versionName "3.7.2"
versionCode 334
versionName "3.7.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
......
......@@ -2,11 +2,11 @@
"config": [
{
"name": "lastUpdate",
"value": "31 Jul 2021"
"value": "23 Aug 2021"
},
{
"name": "version",
"value": 85
"value": 86
},
{
"name": "versionCalibrationBle",
......@@ -115,6 +115,14 @@
"name": "app.displayActivityPass",
"value": false
},
{
"name": "app.activityPass.minRemainingHoursToRegenerate",
"value": 4.0
},
{
"name": "app.wallet.maxCertBeforeWarning",
"value": 15
},
{
"name": "app.isolation.duration",
"value": 691200
......
......@@ -1135,14 +1135,33 @@
"vaccineCompletionController.footer.notifyAndFavorite": "By tapping this button, you will receive a notification on %@, and this certificate will be added as a favorite ❤️ in your TousAntiCovid wallet.",
"activityPass.fullscreen.title": "Pass Activité",
"activityPass.fullscreen.explanation": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
"activityPass.fullscreen.explanation": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n\nExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
"activityPass.fullscreen.readMore": "En savoir plus",
"activityPass.fullscreen.readMore.url": "",
"activityPass.fullscreen.unavailable.alert.title": "Indisponible",
"activityPass.fullscreen.unavailable.alert.message": "Votre connexion ou le service de génération de Pass Activité ne sont pas disponibles.\n\nVous pouvez utiliser le certificat dans l'onglet \"Frontière\".",
"activityPass.fullscreen.unavailable.alert.message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
"activityPass.fullscreen.notYet.alert.title": "Pas encore",
"activityPass.fullscreen.notYet.alert.message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
"activityPass.fullscreen.button.generate": "Générer un Pass Activité",
"europeanCertificate.fullscreen.type.activityPass": "Pass Activité"
"europeanCertificate.fullscreen.type.activityPass": "Pass Activité",
"activityPass.fullscreen.switch.autoRenewal": "Renouvellement auto",
"activityPass.fullscreen.switch.autoRenewal.footer": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
"activityPass.fullscreen.validFor": "Valable encore %@",
"activityPass.fullscreen.validFor.timeFormat": "%dh%dmin",
"activityPass.fullscreen.validFor.timeFormat.hoursMinutes": "%dh%dmin",
"activityPass.fullscreen.validFor.timeFormat.minutes": "%dmin",
"activityPass.fullscreen.validFor.timeFormat.lessThanAMinute": "< 1min",
"activityPassParametersController.title": "Pass Activité",
"activityPassParametersController.switch.notifyMe": "Me notifier",
"activityPassParametersController.button.readCGU": "Lire les CGU",
"activityPassParametersController.doYouConfirm" : "Confirmez-vous la création du Pass Activité ?",
"activityPassParametersController.explanations" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n\nExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
"walletQuantityWarningController.title": "WARNING",
"walletQuantityWarningController.explanation": "The use of TousAntiCovid Wallet is unusual. You have exceeded 15 certificates.\n\n👉 If you are a professional, it is forbidden to use TousAntiCovid to check health passes, under penalty of prosecution (up to 3 years in prison and 45,000 euros fine).\n\nYou must use TousAntiCovid Verif available on the Stores.\n\n👉 If you are an individual, it is recommended to delete some of your certificates which are no longer useful (for example old negative tests).",
"walletQuantityWarningController.continue": "Continue",
"walletQuantityWarningController.continueAlert.title": "Are you sure?",
"walletQuantityWarningController.continueAlert.message": "Despite the risks involved, are you sure you want to continue importing this certificate?",
"walletQuantityWarningController.continueAlert.confirm": "I confirm"
}
......@@ -1110,14 +1110,33 @@
"vaccineCompletionController.footer.notifyAndFavorite": "En appuyant sur ce bouton, vous recevrez une notification le %@, et ce certificat sera ajouté en favori ❤️ dans votre carnet.",
"activityPass.fullscreen.title": "Pass Activité",
"activityPass.fullscreen.explanation": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
"activityPass.fullscreen.explanation": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n\nExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
"activityPass.fullscreen.readMore": "En savoir plus",
"activityPass.fullscreen.readMore.url": "",
"activityPass.fullscreen.unavailable.alert.title": "Indisponible",
"activityPass.fullscreen.unavailable.alert.message": "Votre connexion ou le service de génération de Pass Activité ne sont pas disponibles.\n\nVous pouvez utiliser le certificat dans l'onglet \"Frontière\".",
"activityPass.fullscreen.unavailable.alert.message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
"activityPass.fullscreen.notYet.alert.title": "Pas encore",
"activityPass.fullscreen.notYet.alert.message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
"activityPass.fullscreen.button.generate": "Générer un Pass Activité",
"europeanCertificate.fullscreen.type.activityPass": "Pass Activité"
"europeanCertificate.fullscreen.type.activityPass": "Pass Activité",
"activityPass.fullscreen.switch.autoRenewal": "Renouvellement auto",
"activityPass.fullscreen.switch.autoRenewal.footer": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
"activityPass.fullscreen.validFor": "Valable encore %@",
"activityPass.fullscreen.validFor.timeFormat": "%dh%dmin",
"activityPass.fullscreen.validFor.timeFormat.hoursMinutes": "%dh%dmin",
"activityPass.fullscreen.validFor.timeFormat.minutes": "%dmin",
"activityPass.fullscreen.validFor.timeFormat.lessThanAMinute": "< 1min",
"activityPassParametersController.title": "Pass Activité",
"activityPassParametersController.switch.notifyMe": "Me notifier",
"activityPassParametersController.button.readCGU": "Lire les CGU",
"activityPassParametersController.doYouConfirm" : "Confirmez-vous la création du Pass Activité ?",
"activityPassParametersController.explanations" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n\nExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
"walletQuantityWarningController.title": "ATTENTION",
"walletQuantityWarningController.explanation": "L'usage de TousAntiCovid Carnet est inhabituel. Vous avez dépassé les 15 certificats.\n\n👉 Si vous êtes un professionnel, il est interdit d'utiliser TousAntiCovid pour vérifier les Pass Sanitaires, sous peine de poursuites (jusqu’à 3 ans de prison et 45 000 euros d'amende).\n\nVous devez utiliser TousAntiCovid Verif disponible sur les Stores.\n\n👉 Si vous êtes un particulier, il est recommandé de supprimer certains de vos certificats qui ne sont plus utiles (par exemple des anciens tests négatifs).",
"walletQuantityWarningController.continue": "Continuer",
"walletQuantityWarningController.continueAlert.title": "Êtes-vous sûr ?",
"walletQuantityWarningController.continueAlert.message": "Malgré les risques encourus, êtes-vous sûr de souhaiter importer ce certificat ?",
"walletQuantityWarningController.continueAlert.confirm": "Je confirme"
}
......@@ -207,6 +207,7 @@ class MainActivity : BaseActivity() {
R.id.walletQRCodeFragment,
R.id.universalQrScanFragment,
R.id.vaccineCompletionFragment,
R.id.walletQuantityWarningFragment,
).contains(destination.id).let { noAppBarFragment ->
lifecycleScope.launchWhenResumed {
val windowInsetsController = WindowInsetsControllerCompat(window, window.decorView)
......
......@@ -15,7 +15,6 @@ import android.view.View
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import androidx.preference.PreferenceManager
import com.lunabeestudio.domain.model.WalletCertificateType
import com.lunabeestudio.stopcovid.R
import com.lunabeestudio.stopcovid.coreui.extension.findNavControllerOrNull
......@@ -52,10 +51,6 @@ class PositiveTestStepsFragment : BottomSheetMainFragment() {
requireContext().robertManager()
}
private val sharedPreferences by lazy {
PreferenceManager.getDefaultSharedPreferences(requireContext())
}
private val dccCertificatesManager by lazy {
requireContext().dccCertificatesManager()
}
......
......@@ -39,6 +39,7 @@ import com.lunabeestudio.stopcovid.manager.DeeplinkManager
import com.lunabeestudio.stopcovid.manager.WalletManager
import com.lunabeestudio.stopcovid.model.EuropeanCertificate
import com.lunabeestudio.stopcovid.model.WalletCertificate
import com.lunabeestudio.stopcovid.model.WalletCertificateMalformedException
import com.lunabeestudio.stopcovid.viewmodel.WalletViewModel
import com.lunabeestudio.stopcovid.viewmodel.WalletViewModelFactory
import kotlinx.coroutines.launch
......@@ -75,15 +76,33 @@ class WalletContainerFragment : BaseFragment() {
setupResultListener()
}
private fun handleRawCode(rawCode: String?, certificateFormat: WalletCertificateType.Format?, origin: DeeplinkManager.Origin?) {
private fun handleRawCode(
rawCode: String?,
certificateFormat: WalletCertificateType.Format?,
origin: DeeplinkManager.Origin?,
skipMaxWarning: Boolean = false,
) {
val code = rawCode?.splitUrlFragment()
val certificateValue = code?.getOrNull(0)
val reportCode = code?.getOrNull(1)
val shouldAddCertificate = robertManager.configuration.displaySanitaryCertificatesWallet && certificateValue != null
val shouldReport = reportCode != null && robertManager.isRegistered
val shouldShowMaxCertificatesWarning = shouldAddCertificate && !skipMaxWarning &&
(WalletManager.walletCertificateLiveData.value?.size ?: 0) >= robertManager.configuration.maxCertBeforeWarning
when {
shouldShowMaxCertificatesWarning -> {
setFragmentResultListener(WalletQuantityWarningFragment.MAX_CERTIFICATES_CONFIRM_ADD_RESULT_KEY) { _, bundle ->
val confirmAdd = bundle.getBoolean(WalletQuantityWarningFragment.MAX_CERTIFICATES_CONFIRM_ADD_BUNDLE_KEY, false)
if (confirmAdd) {
handleRawCode(rawCode, certificateFormat, origin, true)
}
}
findNavControllerOrNull()?.safeNavigate(
WalletContainerFragmentDirections.actionWalletContainerFragmentToWalletQuantityWarningFragment()
)
}
shouldAddCertificate && shouldReport -> findNavControllerOrNull()?.safeNavigate(
WalletContainerFragmentDirections.actionWalletContainerFragmentToPositiveTestStepsFragment(
positiveTestDccValue = certificateValue!!,
......@@ -132,12 +151,18 @@ class WalletContainerFragment : BaseFragment() {
val scannedData = bundle.getString(WalletQRCodeFragment.SCANNED_CODE_BUNDLE_KEY)
scannedData?.let { data ->
if (URLUtil.isValidUrl(data)) {
val certificateData = WalletManager.extractCertificateDataFromUrl(data)
handleRawCode(
certificateData.first,
certificateData.second,
null,
)
try {
val certificateData = WalletManager.extractCertificateDataFromUrl(data)
handleRawCode(
certificateData.first,
certificateData.second,
null,
)
} catch (e: WalletCertificateMalformedException) {
lifecycleScope.launch {
handleCertificateError(e, null)
}
}
} else {
handleRawCode(data, null, null)
}
......
/*
* 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/04/05 - for the TOUS-ANTI-COVID project
*/
package com.lunabeestudio.stopcovid.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.setFragmentResult
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.lunabeestudio.stopcovid.coreui.extension.findNavControllerOrNull
import com.lunabeestudio.stopcovid.coreui.fragment.BaseFragment
import com.lunabeestudio.stopcovid.databinding.FragmentWalletQuantityWarningBinding
class WalletQuantityWarningFragment : BaseFragment() {
lateinit var binding: FragmentWalletQuantityWarningBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
super.onCreateView(inflater, container, savedInstanceState)
binding = FragmentWalletQuantityWarningBinding.inflate(inflater, container, false)
ViewCompat.setOnApplyWindowInsetsListener(binding.rootLayout) { v, insets ->
val insetsMask = WindowInsetsCompat.Type.statusBars() or WindowInsetsCompat.Type.displayCutout()
v.updatePadding(
top = insets.getInsets(insetsMask).top
)
insets
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.continueButton.setOnClickListener {
MaterialAlertDialogBuilder(requireContext())
.setTitle(strings["walletQuantityWarningController.continueAlert.title"])
.setMessage(strings["walletQuantityWarningController.continueAlert.message"])
.setPositiveButton(strings["walletQuantityWarningController.continueAlert.confirm"]) { _, _ ->
setFragmentResult(MAX_CERTIFICATES_CONFIRM_ADD_RESULT_KEY, bundleOf(MAX_CERTIFICATES_CONFIRM_ADD_BUNDLE_KEY to true))
findNavControllerOrNull()?.navigateUp()
}
.setNegativeButton(strings["common.cancel"]) { _, _ ->
findNavControllerOrNull()?.navigateUp()
}
.setCancelable(false)
.show()
}
binding.cancelButton.setOnClickListener {
findNavControllerOrNull()?.navigateUp()
}
}
override fun refreshScreen() {
binding.titleTextView.text = strings["walletQuantityWarningController.title"]
binding.explanationTextView.text = strings["walletQuantityWarningController.explanation"]
binding.continueButton.text = strings["walletQuantityWarningController.continue"]
binding.cancelButton.text = strings["common.cancel"]
}
companion object {
const val MAX_CERTIFICATES_CONFIRM_ADD_RESULT_KEY: String = "MAX_CERTIFICATES_CONFIRM_ADD_RESULT_KEY"
const val MAX_CERTIFICATES_CONFIRM_ADD_BUNDLE_KEY: String = "MAX_CERTIFICATES_CONFIRM_ADD_BUNDLE_KEY"
}
}
\ No newline at end of file
......@@ -354,7 +354,7 @@ class EuropeanCertificate private constructor(id: String, value: String, val isF
val verificationResult = VerificationResult()
val plainInput = DefaultPrefixValidationService().decode(value, verificationResult)
val compressedCose = DefaultBase45Service().decode(plainInput, verificationResult)
cose = DefaultCompressorService().decode(compressedCose, verificationResult)
cose = checkNotNull(DefaultCompressorService().decode(compressedCose, verificationResult))
val coseData = checkNotNull(DefaultCoseService().decode(cose, verificationResult))
DefaultSchemaValidator().validate(coseData.cbor, verificationResult)
if (!verificationResult.isSchemaValid) {
......@@ -408,7 +408,7 @@ class EuropeanCertificate private constructor(id: String, value: String, val isF
val verificationResult = VerificationResult()
val plainInput = DefaultPrefixValidationService().decode(value, verificationResult)
val compressedCose = DefaultBase45Service().decode(plainInput, verificationResult)
val cose = DefaultCompressorService().decode(compressedCose, verificationResult)
val cose = checkNotNull(DefaultCompressorService().decode(compressedCose, verificationResult))
val coseData = DefaultCoseService().decode(cose, verificationResult)
val greenCertificate = coseData?.let { DefaultCborService().decode(it.cbor, verificationResult) }
......
<!--
~ 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/30/10 - for the STOP-COVID project
-->
<!--
~ 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/04/05 - for the TOUS-ANTI-COVID project
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_indigo"
android:theme="@style/Theme.StopCovid.Colored"
tools:ignore="Overdraw">
<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foregroundGravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_large"
android:orientation="vertical">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textAppearance="@style/TextAppearance.StopCovid.Title"
tools:text="@tools:sample/lorem[12]" />
</LinearLayout>
</ScrollView>
<com.google.android.material.button.MaterialButton
android:id="@+id/button"
style="@style/Widget.StopCovid.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/spacing_large"
android:layout_marginVertical="@dimen/spacing_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="@tools:sample/lorem[2]" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ 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 - 2021/24/6 - for the TOUS-ANTI-COVID project
-->
<!--
~ 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/04/05 - for the TOUS-ANTI-COVID project
-->
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:theme="@style/Theme.StopCovid.Colored">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_error"
android:paddingHorizontal="@dimen/spacing_large">
<TextView
android:id="@+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/spacing_large"
android:textAppearance="@style/TextAppearance.StopCovid.Title.Bold"