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

Update to 3.5.1

- Favorite DCC ️
- Fallback error to DCC vaccine
- Warning on scan screen for pro
- New wallet logo
parent 72cc2a51
......@@ -21,7 +21,7 @@ import com.lunabeestudio.stopcovid.coreui.extension.safeEmojiSpanify
class CaptionItem : BaseItem<CaptionItem.ViewHolder>(
R.layout.item_caption, CaptionItem::ViewHolder, R.id.item_caption
) {
var text: String? = null
var text: CharSequence? = null
var gravity: Int = Gravity.NO_GRAVITY
@StyleRes
......
......@@ -42,7 +42,7 @@
<dimen name="button_font_size">18sp</dimen>
<dimen name="button_letter_spacing">0</dimen>
<dimen name="badge_size">8dp</dimen>
<dimen name="qr_code_size">200dp</dimen>
<dimen name="qr_code_size">140dp</dimen>
<dimen name="qr_code_widget_size">80dp</dimen>
<dimen name="qr_code_fullscreen_size">240dp</dimen>
......
package com.lunabeestudio.domain.model
class RawWalletCertificate(val type: WalletCertificateType, val value: String, val timestamp: Long)
\ No newline at end of file
data class RawWalletCertificate(
val id: String,
val type: WalletCertificateType,
val value: String,
val timestamp: Long,
var isFavorite: Boolean
)
\ No newline at end of file
......@@ -116,7 +116,7 @@ class SecureKeystoreDataSource(context: Context, private val cryptoManager: Loca
get() = getEncryptedValue(SHARED_PREF_KEY_SAVED_ATTESTATION_DATA, object : TypeToken<Map<String, FormEntry>>() {}.type)
set(value) = setEncryptedValue(SHARED_PREF_KEY_SAVED_ATTESTATION_DATA, value)
private var _attestationsLiveData: MutableLiveData<List<Attestation>?> = MutableLiveData(attestations)
private val _attestationsLiveData: MutableLiveData<List<Attestation>?> = MutableLiveData(attestations)
override val attestationsLiveData: LiveData<List<Attestation>?>
get() = _attestationsLiveData
......@@ -133,7 +133,9 @@ class SecureKeystoreDataSource(context: Context, private val cryptoManager: Loca
setEncryptedValue(SHARED_PREF_KEY_WALLET_CERTIFICATES, value)
_rawWalletCertificatesLiveData.postValue(value)
}
private var _rawWalletCertificatesLiveData: MutableLiveData<List<RawWalletCertificate>?> = MutableLiveData(rawWalletCertificates)
private val _rawWalletCertificatesLiveData: MutableLiveData<List<RawWalletCertificate>?> by lazy {
MutableLiveData(rawWalletCertificates)
}
override val rawWalletCertificatesLiveData: LiveData<List<RawWalletCertificate>?>
get() = _rawWalletCertificatesLiveData
......
......@@ -45,8 +45,8 @@ android {
applicationId "fr.gouv.android.stopcovid"
minSdkVersion 21
targetSdkVersion 30
versionCode 316
versionName "3.5.0"
versionCode 320
versionName "3.5.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
......
{
"config": [
{
{
"name": "lastUpdate",
"value": "16 Jul 2021"
},
......@@ -33,12 +33,16 @@
"value": "v1"
},
{
"name" : "app.cleaUrls",
"value" : ["https://s3.fr-par.scw.cloud/clea-batch/", "https://signal-static.oos.cloudgouv-eu-west-1.outscale.com/", "https://signal-static.tousanticovid.gouv.fr/"]
"name" : "app.cleaUrls",
"value" : ["https://s3.fr-par.scw.cloud/clea-batch/", "https://signal-static.oos.cloudgouv-eu-west-1.outscale.com/", "https://signal-static.tousanticovid.gouv.fr/"]
},
{
"name" : "app.ameliUrl",
"value" : "https://declare.ameli.fr/tousanticovid/t/%@/"
"name" : "app.ameliUrl",
"value" : "https://declare.ameli.fr/tousanticovid/t/%@/"
},
{
"name" : "app.ratingsKeyFiguresOpeningThreshold",
"value" : 10
},
{
"name" : "app.contagiousSpan",
......@@ -97,12 +101,12 @@
{
"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-----",
"AV01": "-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1T9uG2bEP7uWND6RT/lJs2y787BkEJoRMMLXvqPKFFC3ckqFAPnFjbiv/odlWH04a1P9CvaCRxG31FMEOFZyXA==-----END PUBLIC KEY-----",
"AV02": "-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3jL6zQ0aQj9eJHUw4VDHB9sMoviLVIlADnoBwC43Md8p9w655z2bDhYEEajQ2amQzt+eU7HdWrvqY23Do91Izg==-----END PUBLIC KEY-----",
"ING1": "-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaMOVmK4FAlYeU8tUGpQgtpuLgwSn/arX/4ltKt/wYCole7rB4ECRLtSH4domHU/Z1cjUmVfyGH6NcboWrys9ww==-----END PUBLIC KEY-----"
{
"AHP1": "-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPnxJntwNwme9uHSasmGFFwdC0FWNEpucgzhjr+/AZ6UuTm3kL3ogEUAwKU0tShEVmZNK4/lM05h+0ZvtboJM/A==-----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-----",
"ING1": "-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaMOVmK4FAlYeU8tUGpQgtpuLgwSn/arX/4ltKt/wYCole7rB4ECRLtSH4domHU/Z1cjUmVfyGH6NcboWrys9ww==-----END PUBLIC KEY-----"
}
}
]
......@@ -262,7 +266,7 @@
"code":"DEFAULT",
"value":7
},
{
{
"code":"EU/1/20/1525",
"value":28
}
......@@ -303,7 +307,7 @@
{
"name": "ble.dontUseScannerHardwareBatching",
"value": [ "G3121", "E5603", "SM-G981B", "CDY-NX9A", "SM-J530F", "SM-G770F", "SM-G780F", "SM-G781B", "SM-G960F", "SM-G965F", "SM-G970F", "SM-G973F", "SM-G975F", "SM-G977B", "SM-G980F", "SM-G981B", "SM-G985F", "SM-G986B", "SM-G988B", "SM-G991B", "SM-G996B", "SM-G998B", "SM-N770F", "SM-N970F", "SM-N975F", "SM-N976B", "SM-N980F", "SM-N981B", "SM-N985F", "SM-N986B", "SM-F700F", "SM-F707B", "SM-F900F", "SM-F916B", "SM-A025G", "SM-A125F", "SM-A326B", "SM-A415F", "SM-A415FN", "SM-A505FN", "SM-A525F", "SM-A526B", "SM-A715F", "SM-A805F", "SM-M115F", "SM-M315F", "SM-M317F", "SM-M515F", "SM-A202F", "SM-A207F", "SM-G715FN", "SM-J730F", "SM-A515F", "SM-J600FN" ]
},
},
{
"name": "ble.calibration",
"value": [
......
......@@ -859,7 +859,7 @@
"walletController.documents.vaccin":"Vaccination certificate",
"walletController.documents.test":"Test result",
"walletController.whenToUse.title":"Pass: when & how",
"walletController.whenToUse.subtitle":"According to the law of May 31 (on the health crisis), the test or vaccination certificates must be presented at the entrance of an event of more than 1,000 people or to travel (including in Corsica and France Overseas).\n\nFor what concerns the health pass in France 🇫🇷 for events with more than 1,000 people, the following certificates are accepted:\n\n👉 a complete vaccination certificate (all necessary doses) and completed more than 1 week ago for all vaccines, except Janssen/Johnson & Johnson for which it is 4 weeks.\n\n👉 a negative test certificate (RT-PCR or antigen) of less than 48 hours\n\n👉 a positive test certificate (RT-PCR or antigen), at least 11 days and less than 6 months old.\n\nFor travels and validity conditions of your health pass by country, more information on the following link 👇",
"walletController.whenToUse.subtitle":"According to the decree of July 16 2021, the test or vaccination certificates must be presented at a venue or an event subject to the health pass on French territory.\n\nFor what concerns the health pass in France 🇫🇷 requested in these venues or events, the following certificates are accepted:\n\n👉 a complete vaccination certificate (all necessary doses) and completed more than 1 week ago for all vaccines, except Janssen/Johnson & Johnson for which it is 4 weeks.\n\n👉 a negative test certificate (RT-PCR or antigen) of less than 48 hours\n\n👉 a positive RT-PCR test certificate (not antigen), at least 11 days and less than 6 months old.\n\nFor travels and validity conditions of your health pass by country, more information on the following link 👇",
"walletController.whenToUse.button":"More on travels",
"walletController.whenToUse.url":"https://bonjour.tousanticovid.gouv.fr/voyager.html",
"walletController.phone.title":"Need help?",
......@@ -1005,7 +1005,7 @@
"europeanCertificate.fullscreen.type.minimum": "Activity",
"europeanCertificate.fullscreen.type.border": "Border",
"europeanCertificate.fullscreen.type.border.switch": "Border mode",
"europeanCertificate.fullscreen.type.minimum.footer" : "👉 This screen with the QR Code has to be used in the scope of the health pass in France for activities with more than 1,000 people or night clubs. For a border crossing, please switch to the \"Border\" mode.",
"europeanCertificate.fullscreen.type.minimum.footer" : "👉 This screen with the QR Code has to be presented at a venue or event subject to the health pass on French territory. For a border crossing, please switch to the \"Border\" mode.",
"europeanCertificate.fullscreen.englishDescription.vaccine": "<FULL_NAME>\nDate of birth <BIRTHDATE>\n<VACCINE_NAME>\nInjection date: <DATE>",
"europeanCertificate.fullscreen.englishDescription.recovery": "<FULL_NAME>\nDate of birth <BIRTHDATE>\nTested on <DATE>",
"europeanCertificate.fullscreen.englishDescription.test": "<FULL_NAME>\nDate of birth <BIRTHDATE>\n<ANALYSIS_RESULT> - <ANALYSIS_CODE>\nTested on <FROM_DATE>",
......@@ -1033,5 +1033,12 @@
"userLanguageController.title": "Language",
"userLanguageController.subtitle": "Please select the language you prefer:",
"userLanguageController.footer": "You can change this later in the App’s parameters section.",
"userLanguageController.button.title":"OK"
"userLanguageController.button.title":"OK",
"accessibility.hint.addToFavorite": "Add as favorite",
"flashWalletCodeController.footer.text": "⚠️ For professionals, certificate verification is only allowed with the “TAC Verification” application that you can find on the Stores.\nTap to find out more.",
"flashWalletCodeController.footer.link.ios": "https://apps.apple.com/fr/app/tousanticovid-verif/id1562303493",
"flashWalletCodeController.footer.link.android": "https://play.google.com/store/apps/details?id=com.ingroupe.verify.anticovid",
"universalQrScanController.footer.text": "⚠️ For professionals, certificate verification is only allowed with the “TAC Verification” application that you can find on the Stores.\nTap to find out more.",
"universalQrScanController.footer.link.ios": "https://apps.apple.com/fr/app/tousanticovid-verif/id1562303493",
"universalQrScanController.footer.link.android": "https://play.google.com/store/apps/details?id=com.ingroupe.verify.anticovid"
}
{
{
"app.name":"TousAntiCovid",
"onboarding.welcomeController.title":"Bienvenue",
"onboarding.welcomeController.mainMessage.title":"Soyez alertés et alertez les autres en cas d’exposition à la COVID-19",
......@@ -823,7 +823,7 @@
"walletController.documents.vaccin":"Attestation de vaccin",
"walletController.documents.test":"Résultat de test",
"walletController.whenToUse.title":"Pass sanitaire : quand et comment ?",
"walletController.whenToUse.subtitle":"Selon la loi du 31 mai relative à la gestion de la sortie de crise sanitaire, les certificats de test ou de vaccination devront être présentés en France à l’entrée d’un événement de plus de 1 000 personnes ou pour voyager (y compris en Corse et en Outre-mer).\n\nDans le cadre du pass sanitaire conditionnant l'accès aux événements de plus de 1 000 personnes en France 🇫🇷, il est accepté :\n\n👉 un certificat de vaccination complet (toutes les doses nécessaires) et réalisé il y a plus de 1 semaine pour tous les vaccins, sauf Janssen/Johnson & Johnson pour lequel c’est 4 semaines.\n\n👉 un certificat de test RT-PCR ou antigénique négatif de moins de 48h\n\n👉 un certificat de test RT-PCR ou antigénique positif, datant d'au moins 11 jours et de moins de 6 mois.\n\nPour les voyages et les conditions de validité du pass sanitaire par pays, plus d'information sur le lien suivant 👇",
"walletController.whenToUse.subtitle":"Selon le décret du 16 juillet 2021, les certificats de test ou de vaccination devront être présentés en France pour les lieux et activités soumis au pass sanitaire sur le territoire français.\n\nDans le cadre du pass sanitaire conditionnant l'accès à ces lieux et activités en France 🇫🇷, il est accepté :\n\n👉 un certificat de vaccination complet (toutes les doses nécessaires) et réalisé il y a plus de 1 semaine pour tous les vaccins, sauf Janssen/Johnson & Johnson pour lequel c’est 4 semaines.\n\n👉 un certificat de test RT-PCR ou antigénique négatif de moins de 48h\n\n👉 un certificat de test RT-PCR (pas antigénique) positif, datant d'au moins 11 jours et de moins de 6 mois.\n\nPour les voyages et les conditions de validité du pass sanitaire par pays, plus d'information sur le lien suivant 👇",
"walletController.whenToUse.button":"Infos voyages",
"walletController.whenToUse.url":"https://bonjour.tousanticovid.gouv.fr/voyager.html",
"walletController.phone.title":"Besoin d'aide ?",
......@@ -979,7 +979,7 @@
"europeanCertificate.fullscreen.type.minimum": "Activité",
"europeanCertificate.fullscreen.type.border": "Frontière",
"europeanCertificate.fullscreen.type.border.switch": "Mode frontière",
"europeanCertificate.fullscreen.type.minimum.footer" : "👉 Cet affichage du QR Code est à utiliser dans le cadre du pass sanitaire sur le territoire français pour les activités de plus de 1 000 personnes et les boîtes de nuit. Pour un passage de frontière, veuillez passer en mode \"Frontière\".",
"europeanCertificate.fullscreen.type.minimum.footer" : "👉 Cet affichage du QR Code est à utiliser pour les lieux et activités soumis au pass sanitaire sur le territoire français. Pour un passage de frontière, veuillez passer en mode \"Frontière\".",
"europeanCertificate.fullscreen.englishDescription.vaccine": "<FULL_NAME>\nDate of birth <BIRTHDATE>\n<VACCINE_NAME>\nInjection date: <DATE>",
"europeanCertificate.fullscreen.englishDescription.recovery": "<FULL_NAME>\nDate of birth <BIRTHDATE>\nTested on <DATE>",
"europeanCertificate.fullscreen.englishDescription.test": "<FULL_NAME>\nDate of birth <BIRTHDATE>\n<ANALYSIS_RESULT> - <ANALYSIS_CODE>\nTested on <FROM_DATE>",
......@@ -1008,5 +1008,12 @@
"userLanguageController.title":"Langue",
"userLanguageController.subtitle":"Veuillez sélectionner la langue que vous préférez :",
"userLanguageController.footer":"Vous pouvez modifier cela plus tard dans les paramètres de l'application.",
"userLanguageController.button.title":"OK"
"userLanguageController.button.title":"OK",
"accessibility.hint.addToFavorite": "Ajouter en favori",
"flashWalletCodeController.footer.text": "⚠️ Pour les professionnels, la vérification de certificats est uniquement permise avec l’application “TAC Vérif” que vous pouvez trouver sur les Stores.\nAppuyez pour en savoir plus.",
"flashWalletCodeController.footer.link.ios": "https://apps.apple.com/fr/app/tousanticovid-verif/id1562303493",
"flashWalletCodeController.footer.link.android": "https://play.google.com/store/apps/details?id=com.ingroupe.verify.anticovid",
"universalQrScanController.footer.text": "⚠️ Pour les professionnels, la vérification de certificats est uniquement permise avec l’application “TAC Vérif” que vous pouvez trouver sur les Stores.\nAppuyez pour en savoir plus.",
"universalQrScanController.footer.link.ios": "https://apps.apple.com/fr/app/tousanticovid-verif/id1562303493",
"universalQrScanController.footer.link.android": "https://play.google.com/store/apps/details?id=com.ingroupe.verify.anticovid"
}
......@@ -309,7 +309,8 @@ class MainActivity : BaseActivity() {
fun showLanguageDialogIfNeeded() {
if (sharedPrefs.userLanguage == null &&
UiConstants.SUPPORTED_LOCALES.map { it.language }.none { it == Locale.getDefault().language }) {
UiConstants.SUPPORTED_LOCALES.map { it.language }.none { it == Locale.getDefault().language }
) {
val userLanguageSelectionView = DialogUserLanguageBinding.inflate(layoutInflater)
......
......@@ -187,4 +187,4 @@ fun SanitaryCertificate.validityString(configuration: Configuration, strings: Lo
}
val WalletCertificate.raw: RawWalletCertificate
get() = RawWalletCertificate(type, value, timestamp)
\ No newline at end of file
get() = RawWalletCertificate(id, type, value, timestamp, (this as? EuropeanCertificate)?.isFavorite ?: false)
\ No newline at end of file
......@@ -16,12 +16,15 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageSwitcher
import androidx.annotation.ColorInt
import androidx.annotation.DimenRes
import androidx.annotation.DrawableRes
import androidx.core.view.isInvisible
import androidx.recyclerview.widget.RecyclerView
import com.lunabeestudio.stopcovid.R
import com.lunabeestudio.stopcovid.coreui.extension.toDimensSize
import com.lunabeestudio.stopcovid.databinding.ItemLogoBinding
import com.mikepenz.fastadapter.binding.AbstractBindingItem
import kotlin.math.roundToInt
class LogoItem : AbstractBindingItem<ItemLogoBinding>() {
override val type: Int = R.id.item_logo
......@@ -35,6 +38,9 @@ class LogoItem : AbstractBindingItem<ItemLogoBinding>() {
var onClick: (() -> Unit)? = null
@DimenRes
var minLogoHeightRes: Int = R.dimen.min_logo_height
override fun createBinding(inflater: LayoutInflater, parent: ViewGroup?): ItemLogoBinding {
return ItemLogoBinding.inflate(inflater, parent, false)
}
......@@ -50,6 +56,11 @@ class LogoItem : AbstractBindingItem<ItemLogoBinding>() {
binding.imageSwitcher.setOnClickListener {
onClick?.let { it1 -> it1() }
}
binding.imageSwitcher.minimumHeight = if (minLogoHeightRes == NO_MINIMUM_HEIGHT) {
0
} else {
minLogoHeightRes.toDimensSize(binding.imageSwitcher.context).roundToInt()
}
}
override fun unbindView(binding: ItemLogoBinding) {
......@@ -58,6 +69,10 @@ class LogoItem : AbstractBindingItem<ItemLogoBinding>() {
binding.imageView2.imageTintList = null
}
companion object {
const val NO_MINIMUM_HEIGHT: Int = -1
}
class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
val imageSwitcher: ImageSwitcher = v.findViewById(R.id.imageSwitcher)
......
......@@ -45,6 +45,10 @@ class QrCodeCardItem : AbstractBindingItem<ItemQrCodeCardBinding>() {
var actionContentDescription: String? = null
var onTag1Click: (() -> Unit)? = null
var onTag2Click: (() -> Unit)? = null
var favoriteContentDescription: String? = null
var favoriteState: FavoriteState = FavoriteState.HIDDEN
var onFavoriteClick: (() -> Unit)? = null
var bottomText: String? = null
override val type: Int = R.id.item_attestation_qr_code
......@@ -106,6 +110,21 @@ class QrCodeCardItem : AbstractBindingItem<ItemQrCodeCardBinding>() {
binding.tagLayout.isVisible = !(tag1Text.isNullOrEmpty() && tag2Text.isNullOrEmpty())
binding.actionButton.contentDescription = actionContentDescription
binding.favoriteButton.isVisible = favoriteState != FavoriteState.HIDDEN
binding.favoriteButton.contentDescription = favoriteContentDescription
binding.favoriteButton.setOnClickListener {
onFavoriteClick?.invoke()
}
when (favoriteState) {
FavoriteState.HIDDEN -> {
/* no-op */
}
FavoriteState.NOT_CHECKED -> binding.favoriteButton.setImageResource(R.drawable.ic_empty_heart)
FavoriteState.CHECKED -> binding.favoriteButton.setImageResource(R.drawable.ic_filled_heart)
}
binding.bottomActionTextView.setTextOrHide(bottomText)
}
override fun unbindView(binding: ItemQrCodeCardBinding) {
......@@ -152,6 +171,10 @@ class QrCodeCardItem : AbstractBindingItem<ItemQrCodeCardBinding>() {
else -> false
}
}
enum class FavoriteState {
HIDDEN, NOT_CHECKED, CHECKED
}
}
fun qrCodeCardItem(block: (QrCodeCardItem.() -> Unit)): QrCodeCardItem = QrCodeCardItem().apply(
......
package com.lunabeestudio.stopcovid.fastitem
import android.graphics.Bitmap
import android.view.LayoutInflater
import android.view.ViewGroup
import com.lunabeestudio.stopcovid.R
import com.lunabeestudio.stopcovid.coreui.extension.setTextOrHide
import com.lunabeestudio.stopcovid.databinding.ItemSmallQrCodeCardBinding
import com.mikepenz.fastadapter.binding.AbstractBindingItem
class SmallQrCodeCardItem : AbstractBindingItem<ItemSmallQrCodeCardBinding>() {
override val type: Int = R.id.item_small_qr_code_card_item
var generateBarcode: (() -> Bitmap)? = null
var title: String? = null
var body: String? = null
var onClick: (() -> Unit)? = null
override fun createBinding(inflater: LayoutInflater, parent: ViewGroup?): ItemSmallQrCodeCardBinding {
return ItemSmallQrCodeCardBinding.inflate(inflater, parent, false)
}
override fun bindView(binding: ItemSmallQrCodeCardBinding, payloads: List<Any>) {
super.bindView(binding, payloads)
binding.titleTextView.setTextOrHide(title)
binding.bodyTextView.setTextOrHide(body)
val bitmap = generateBarcode?.invoke()
binding.qrCodeImageView.setImageBitmap(bitmap)
binding.container.setOnClickListener {
onClick?.invoke()
}
}
}
fun smallQrCodeCardItem(block: (SmallQrCodeCardItem.() -> Unit)): SmallQrCodeCardItem = SmallQrCodeCardItem().apply(block)
\ No newline at end of file
......@@ -222,6 +222,7 @@ class AttestationsFragment : MainFragment() {
)
}
actionContentDescription = strings["accessibility.hint.otherActions"]
bottomText = strings["walletController.favoriteCertificateSection.openFullScreen"]
identifier = mainDescription.hashCode().toLong()
}
}
......
......@@ -24,7 +24,6 @@ import com.lunabeestudio.stopcovid.coreui.extension.appCompatActivity
import com.lunabeestudio.stopcovid.coreui.extension.findParentFragmentByType
import com.lunabeestudio.stopcovid.coreui.extension.toDimensSize
import com.lunabeestudio.stopcovid.databinding.FragmentFullscreenDccBinding
import com.lunabeestudio.stopcovid.extension.dccCertificatesManager
import com.lunabeestudio.stopcovid.extension.formatDccText
import com.lunabeestudio.stopcovid.extension.fullName
import com.lunabeestudio.stopcovid.extension.isFrench
......@@ -48,16 +47,12 @@ class FullscreenDccFragment : ForceLightFragment(R.layout.fragment_fullscreen_dc
requireContext().secureKeystoreDataSource()
}
private val dccCertificatesManager by lazy {
requireContext().dccCertificatesManager()
}
private val viewModel: WalletViewModel by viewModels(
{
findParentFragmentByType<WalletContainerFragment>() ?: requireParentFragment()
},
{
WalletViewModelFactory(robertManager, keystoreDataSource, dccCertificatesManager)
WalletViewModelFactory(robertManager, keystoreDataSource)
}
)
......@@ -78,7 +73,7 @@ class FullscreenDccFragment : ForceLightFragment(R.layout.fragment_fullscreen_dc
.map { certificates ->
certificates
?.filterIsInstance<EuropeanCertificate>()
?.firstOrNull { it.value == args.certificateValue }
?.firstOrNull { it.id == args.id }
}
.observe(viewLifecycleOwner) { europeanCertificate ->
this.europeanCertificate = europeanCertificate
......@@ -129,4 +124,4 @@ class FullscreenDccFragment : ForceLightFragment(R.layout.fragment_fullscreen_dc
}
explanationTextView.isVisible = !isChecked
}
}
\ No newline at end of file
}
......@@ -46,6 +46,8 @@ import androidx.navigation.navOptions
import androidx.preference.PreferenceManager
import com.airbnb.lottie.utils.Utils
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.zxing.BarcodeFormat
import com.journeyapps.barcodescanner.BarcodeEncoder
import com.lunabeestudio.analytics.manager.AnalyticsManager
import com.lunabeestudio.analytics.model.AppEventName
import com.lunabeestudio.robert.RobertApplication
......@@ -61,6 +63,7 @@ import com.lunabeestudio.stopcovid.coreui.extension.appCompatActivity
import com.lunabeestudio.stopcovid.coreui.extension.findNavControllerOrNull
import com.lunabeestudio.stopcovid.coreui.extension.isNightMode
import com.lunabeestudio.stopcovid.coreui.extension.refreshLift
import com.lunabeestudio.stopcovid.coreui.extension.toDimensSize
import com.lunabeestudio.stopcovid.coreui.extension.viewLifecycleOwnerOrNull
import com.lunabeestudio.stopcovid.coreui.fastitem.CardWithActionsItem
import com.lunabeestudio.stopcovid.coreui.fastitem.cardWithActionItem
......@@ -102,6 +105,7 @@ import com.lunabeestudio.stopcovid.fastitem.logoItem
import com.lunabeestudio.stopcovid.fastitem.numbersCardItem
import com.lunabeestudio.stopcovid.fastitem.onOffLottieItem
import com.lunabeestudio.stopcovid.fastitem.proximityButtonItem
import com.lunabeestudio.stopcovid.fastitem.smallQrCodeCardItem
import com.lunabeestudio.stopcovid.manager.AppMaintenanceManager
import com.lunabeestudio.stopcovid.manager.DeeplinkManager
import com.lunabeestudio.stopcovid.manager.InfoCenterManager
......@@ -154,6 +158,8 @@ class ProximityFragment : TimeMainFragment() {
PreferenceManager.getDefaultSharedPreferences(requireContext())
}
private val barcodeEncoder by lazy(LazyThreadSafetyMode.NONE) { BarcodeEncoder() }
private var activityResultLauncher: ActivityResultLauncher<Intent>? = null
private var onOffLottieItem: OnOffLottieItem? = null
......@@ -402,6 +408,9 @@ class ProximityFragment : TimeMainFragment() {
viewModel.activeAttestationCount.observeEventAndConsume(viewLifecycleOwner) {
refreshScreen()
}
viewModel.favoriteDcc.observeEventAndConsume(viewLifecycleOwner) {
refreshScreen()
}
}
private fun initHasNewsObserver() {
......@@ -866,11 +875,11 @@ class ProximityFragment : TimeMainFragment() {
findNavControllerOrNull()?.safeNavigate(ProximityFragmentDirections.actionProximityFragmentToAttestationsFragment())
}
mainTitle = strings["home.attestationSection.cell.title"]
val attestationCount = viewModel.activeAttestationCount.value
mainBody = when (val count = attestationCount?.peekContent()) {
val attestationCount = viewModel.activeAttestationCount.value?.peekContent()
mainBody = when (attestationCount) {
0, null -> strings["home.attestationSection.cell.subtitle.noAttestations"]
1 -> strings["home.attestationSection.cell.subtitle.oneAttestation"]
else -> stringsFormat("home.attestationSection.cell.subtitle.multipleAttestations", count)
else -> stringsFormat("home.attestationSection.cell.subtitle.multipleAttestations", attestationCount)
}
identifier = R.drawable.attestation_card.toLong()
}
......@@ -884,6 +893,33 @@ class ProximityFragment : TimeMainFragment() {
}
if (robertManager.configuration.displaySanitaryCertificatesWallet) {
val favoriteDcc = viewModel.favoriteDcc.value?.peekContent()
if (favoriteDcc != null) {
items += smallQrCodeCardItem {
val qrCodeSize = R.dimen.card_image_height.toDimensSize(requireContext()).toInt()
title = strings["home.walletSection.favoriteCertificate.cell.title"]
body = strings["home.walletSection.favoriteCertificate.cell.subtitle"]
generateBarcode = {
barcodeEncoder.encodeBitmap(
favoriteDcc.value,
BarcodeFormat.QR_CODE,
qrCodeSize,
qrCodeSize,
)
}
onClick = {
findNavControllerOrNull()?.safeNavigate(
ProximityFragmentDirections.actionProximityFragmentToFullscreenDccFragment(favoriteDcc.id)
)
}
identifier = favoriteDcc.id.hashCode().toLong()
}
items += spaceItem {
spaceRes = R.dimen.spacing_medium
identifier = items.count().toLong()
}
}
items += cardWithActionItem(CardTheme.Primary) {
mainImage = R.drawable.wallet_card
mainLayoutDirection = LayoutDirection.RTL
......
......@@ -24,6 +24,7 @@ import com.journeyapps.barcodescanner.BarcodeResult
import com.lunabeestudio.stopcovid.coreui.extension.appCompatActivity
import com.lunabeestudio.stopcovid.coreui.extension.findNavControllerOrNull
import com.lunabeestudio.stopcovid.coreui.extension.openAppSettings
import com.lunabeestudio.stopcovid.coreui.extension.setTextOrHide
import com.lunabeestudio.stopcovid.coreui.extension.showPermissionRationale
import com.lunabeestudio.stopcovid.coreui.fragment.BaseFragment
import com.lunabeestudio.stopcovid.databinding.FragmentQrCodeBinding
......@@ -33,7 +34,9 @@ abstract class QRCodeFragment : BaseFragment() {
private var permissionResultLauncher: ActivityResultLauncher<String>? = null
abstract fun getTitleKey(): String
abstract fun getExplanationKey(): String
abstract val explanationKey: String
abstract val footerKey: String?
abstract fun onFooterClick()
abstract fun onCodeScanned(code: String)
protected var isReadyToStartScanFlow: Boolean = true
......@@ -151,6 +154,8 @@ abstract class QRCodeFragment : BaseFragment() {
}
override fun refreshScreen() {
binding?.title?.text = strings[getExplanationKey()]
binding?.title?.setTextOrHide(strings[explanationKey])
binding?.footer?.setTextOrHide(footerKey?.let(strings::get).takeIf { !it.isNullOrBlank() })
binding?.footer?.setOnClickListener { onFooterClick() }
}
}
\ No newline at end of file
......@@ -18,7 +18,9 @@ import com.lunabeestudio.stopcovid.extension.showInvalidCodeAlert
class ReportQRCodeFragment : QRCodeFragment() {
override fun getTitleKey(): String = "declareController.title"
override fun getExplanationKey(): String = "scanCodeController.explanation"
override val explanationKey: String = "scanCodeController.explanation"
override val footerKey: String? = null
override fun onFooterClick() {}
override fun onCodeScanned(code: String) {
if (!code.isReportCodeValid()) {
......
......@@ -18,10 +18,15 @@ import androidx.fragment.app.setFragmentResult
import androidx.preference.PreferenceManager
import com.lunabeestudio.stopcovid.coreui.extension.findNavControllerOrNull
import com.lunabeestudio.stopcovid.extension.hasUsedUniversalQrScan
import com.lunabeestudio.stopcovid.extension.openInExternalBrowser
import com.lunabeestudio.stopcovid.extension.safeNavigate
class UniversalQrScanFragment : QRCodeFragment() {
override fun getTitleKey(): String = "universalQrScanController.title"
override val explanationKey: String = "universalQrScanController.explanation"
override val footerKey: String = "universalQrScanController.footer.text"
private val sharedPrefs: SharedPreferences by lazy {
PreferenceManager.getDefaultSharedPreferences(requireContext())
}
......@@ -37,9 +42,6 @@ class UniversalQrScanFragment : QRCodeFragment() {
}
}
override fun getTitleKey(): String = "universalQrScanController.title"
override fun getExplanationKey(): String = "universalQrScanController.explanation"
override fun onCodeScanned(code: String) {
setFragmentResult(
SCANNED_CODE_RESULT_KEY,
......@@ -48,6 +50,11 @@ class UniversalQrScanFragment : QRCodeFragment() {
findNavControllerOrNull()?.popBackStack()
}
override fun onFooterClick() {
strings["universalQrScanController.footer.link.android"]?.openInExternalBrowser(requireContext())
findNavControllerOrNull()?.popBackStack()
}
companion object {
const val SCANNED_CODE_RESULT_KEY: String = "UNIVERSAL_QR_SCAN_FRAGMENT.SCANNED_CODE_RESULT_KEY"
const val SCANNED_CODE_BUNDLE_KEY: String = "UNIVERSAL_QR_SCAN_FRAGMENT.SCANNED_CODE_BUNDLE_KEY"
......