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

Update to 3.7.5

- Charts adjustements
- Confetti on Logo scan
parent 87b7717e
......@@ -33,8 +33,12 @@ object ConfigConstant {
}
object KeyFigures {
const val MASTER_URL: String = BASE_URL + "infos/key-figures.json"
const val MASTER_LOCAL_FILENAME: String = "key-figures.json"
const val NATIONAL_SUFFIX: String = "nat"
const val LOCAL_FILENAME_TEMPLATE: String = "key-figures-%s.pb.gz"
const val URL: String = BASE_URL + "infos/v2%s/$LOCAL_FILENAME_TEMPLATE"
}
object MoreKeyFigures {
const val FOLDER: String = "MoreKeyFigures/"
const val URL: String = VERSIONED_SERVER_URL + FOLDER
const val FILE_PREFIX: String = "morekeyfigures-"
......@@ -161,6 +165,7 @@ object ConfigConstant {
object Store {
const val GOOGLE: String = "market://details?id=fr.gouv.android.stopcovid"
const val HUAWEI: String = "appmarket://details?id=fr.gouv.android.stopcovid"
const val WEBSITE: String = "https://bonjour.tousanticovid.gouv.fr"
const val TAC_WEBSITE: String = "https://bonjour.tousanticovid.gouv.fr"
const val STOPCOVID_WEBSITE: String = "https://bonjour.stopcovid.gouv.fr"
}
}
......@@ -74,20 +74,20 @@ abstract class FastAdapterFragment : BaseFragment() {
protected fun showLoading(loadingText: String? = null) {
binding?.recyclerView?.isVisible = false
binding?.emptyLayout?.isVisible = false
binding?.emptyLayout?.root?.isVisible = false
binding?.loadingLayout?.isVisible = true
binding?.loadingDescriptionTextView?.setTextOrHide(loadingText)
}
protected fun showEmpty() {
binding?.recyclerView?.isVisible = false
binding?.emptyLayout?.isVisible = true
binding?.emptyLayout?.root?.isVisible = true
binding?.loadingLayout?.isVisible = false
}
protected fun showData() {
binding?.recyclerView?.isVisible = true
binding?.emptyLayout?.isVisible = false
binding?.emptyLayout?.root?.isVisible = false
binding?.loadingLayout?.isVisible = false
}
......
......@@ -10,7 +10,7 @@
package com.lunabeestudio.stopcovid.coreui.model
class ApiDaysAfterCompletionEntry(
class ApiCodeValueEntry(
val code: String,
val value: Int,
)
\ No newline at end of file
......@@ -141,6 +141,8 @@ internal class ApiConfiguration(
val conversionApiVersion: Int,
@SerializedName("app.wallet.maxCertBeforeWarning")
val maxCertBeforeWarning: Int,
@SerializedName("app.wallet.vaccin.noWaitDoses")
val noWaitDoses: String,
)
internal fun ApiConfiguration.toDomain(gson: Gson) = Configuration(
......@@ -212,18 +214,23 @@ internal fun ApiConfiguration.toDomain(gson: Gson) = Configuration(
cleaUrls,
object : TypeToken<List<String>?>() {}.type
),
covidPlusWarning = covidPlusWarning,
covidPlusNoTracing = covidPlusNoTracing,
displayCertificateConversion = displayCertificateConversion,
daysAfterCompletion = (
gson.fromJson(
daysAfterCompletion,
object : TypeToken<List<ApiDaysAfterCompletionEntry>>() {}.type
) as List<ApiDaysAfterCompletionEntry>
object : TypeToken<List<ApiCodeValueEntry>>() {}.type
) as List<ApiCodeValueEntry>
).associate { Pair(it.code, it.value) },
certificateConversionSidepOnlyCode = gson.fromJson(certificateConversionSidepOnlyCode, object : TypeToken<List<String>>() {}.type),
conversionPublicKey = gson.fromJson(conversionPublicKey, object : TypeToken<Map<String, String>>() {}.type),
conversionApiVersion = conversionApiVersion,
maxCertBeforeWarning = maxCertBeforeWarning,
noWaitDoses = (
gson.fromJson(
noWaitDoses,
object : TypeToken<List<ApiCodeValueEntry>>() {}.type
) as List<ApiCodeValueEntry>
).associate { Pair(it.code, it.value) },
)
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/7/9 - for the TOUS-ANTI-COVID project
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/emptyLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible"
tools:showIn="@layout/fragment_recycler_view">
<ImageView
android:id="@+id/emptyImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/spacing_medium"
android:importantForAccessibility="no"
android:scaleType="centerInside"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/emptyTitleTextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/emptyTitleTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/spacing_large"
android:layout_marginVertical="@dimen/spacing_medium"
android:gravity="center"
android:textAppearance="@style/TextAppearance.StopCovid.Title"
app:layout_constraintBottom_toTopOf="@id/emptyDescriptionTextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/emptyImageView"
app:layout_constraintVertical_chainStyle="packed"
tools:text="@tools:sample/lorem[5]" />
<TextView
android:id="@+id/emptyDescriptionTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/spacing_large"
android:layout_marginVertical="@dimen/spacing_medium"
android:gravity="center"
android:textAppearance="@style/TextAppearance.StopCovid.Caption"
app:layout_constraintBottom_toTopOf="@id/emptyButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/emptyTitleTextView"
tools:text="@tools:sample/lorem[15]" />
<com.google.android.material.button.MaterialButton
android:id="@+id/emptyButton"
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"
app:layout_constraintTop_toBottomOf="@id/emptyDescriptionTextView"
tools:text="@tools:sample/lorem[2]" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -20,72 +20,9 @@
android:layout_height="match_parent"
android:clipToPadding="false" />
<androidx.constraintlayout.widget.ConstraintLayout
<include
android:id="@+id/emptyLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible">
<ImageView
android:id="@+id/emptyImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/spacing_medium"
android:importantForAccessibility="no"
android:scaleType="centerInside"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/emptyTitleTextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/emptyTitleTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/spacing_large"
android:layout_marginVertical="@dimen/spacing_medium"
android:gravity="center"
android:textAppearance="@style/TextAppearance.StopCovid.Title"
app:layout_constraintBottom_toTopOf="@id/emptyDescriptionTextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/emptyImageView"
app:layout_constraintVertical_chainStyle="packed"
tools:text="@tools:sample/lorem[5]" />
<TextView
android:id="@+id/emptyDescriptionTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/spacing_large"
android:layout_marginVertical="@dimen/spacing_medium"
android:gravity="center"
android:textAppearance="@style/TextAppearance.StopCovid.Caption"
app:layout_constraintBottom_toTopOf="@id/emptyButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/emptyTitleTextView"
tools:text="@tools:sample/lorem[15]" />
<com.google.android.material.button.MaterialButton
android:id="@+id/emptyButton"
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"
app:layout_constraintTop_toBottomOf="@id/emptyDescriptionTextView"
tools:text="@tools:sample/lorem[2]" />
</androidx.constraintlayout.widget.ConstraintLayout>
layout="@layout/empty_view" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/loadingLayout"
......
......@@ -72,4 +72,5 @@ class Configuration(
var conversionPublicKey: Map<String, String>,
var conversionApiVersion: Int,
var maxCertBeforeWarning: Int,
var noWaitDoses: Map<String, Int>,
)
......@@ -12,9 +12,14 @@ package com.lunabeestudio.robert.extension
import timber.log.Timber
inline fun <reified T : Enum<T>> safeEnumValueOf(name: String?): T? {
inline fun <reified T : Enum<T>> safeEnumValueOf(name: String?, uppercase: Boolean = true): T? {
return try {
enumValueOf<T>(name!!)
val fixedName = if (uppercase) {
name!!.uppercase()
} else {
name!!
}
enumValueOf<T>(fixedName)
} catch (e: IllegalArgumentException) {
Timber.e("Failed to get enum ${T::class.java.simpleName} for string \"$name\"")
null
......
......@@ -9,7 +9,7 @@
*/
plugins {
id("de.fayard.refreshVersions") version "0.20.0"
id("de.fayard.refreshVersions") version "0.21.0"
}
rootProject.name = 'TousAntiCovid'
......
......@@ -13,6 +13,7 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt'
apply plugin: 'androidx.navigation.safeargs.kotlin'
apply plugin: 'com.google.protobuf'
android {
compileSdkVersion 30
......@@ -45,8 +46,8 @@ android {
applicationId "fr.gouv.android.stopcovid"
minSdkVersion 21
targetSdkVersion 30
versionCode 344
versionName "3.7.4"
versionCode 358
versionName "3.7.5"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
......@@ -56,6 +57,24 @@ android {
}
buildFeatures.viewBinding = true
def props = new Properties()
file("../versions.properties").withInputStream { props.load(it) }
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:${props.getProperty("version.com.google.protobuf..protobuf-javalite")}"
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option "lite"
}
}
}
}
}
}
dependencies {
......@@ -79,6 +98,8 @@ dependencies {
implementation "nl.dionsegijn:konfetti:_"
implementation 'com.google.protobuf:protobuf-javalite:_'
implementation project(path: ':analytics')
implementation project(path: ':coreui')
implementation project(path: ':framework')
......
......@@ -24,6 +24,9 @@
-keep public class com.lunabeestudio.** { *; }
-keep public class dgca.verifier.app.decoder.model.** { *; }
# Protobuf https://github.com/protocolbuffers/protobuf/issues/6463#issuecomment-632884075
-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }
# Bouncy Castle -- Keep ECDH
-keep class org.bouncycastle.jcajce.provider.asymmetric.EC$* { *; }
-keep class org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi$ECDH { *; }
......
......@@ -298,7 +298,7 @@
]
},
{
"name":"app.wallet.vaccin.noWaitPeriod",
"name":"app.wallet.vaccin.noWaitDoses",
"value":[
{
"code":"DEFAULT",
......
[
{
"section": "Sources",
"description": "Health data comes from the following sources:\n- data from the SI-DEP database,\n- data reported by health centers participating in SI-VIC and the OSCOUR® network,\n- data from the Vaccin Covid database.\n- data from the DRESS (Direction de la recherche, des études de l'évaluation et des statistiques).\n\nAll these data are aggregated and analyzed by Santé publique France (SpF) and made available on Data.gouv.fr",
"section": "Data sources",
"description": "Health data comes from the following sources:\n- data from the SI-DEP database,\n- data reported by health centers participating in SI-VIC and the OSCOUR® network,\n- data from the Vaccin Covid database.\n- data from the DRESS (Direction de la recherche, des études de l'évaluation et des statistiques).\n\nThe data on vaccination and the epidemiological situation are aggregated and analyzed by Santé publique France (SpF) and made available on data.gouv.fr. Data on the number of tests and hospital admissions by vaccination status are produced by the DREES.",
"links": [
{
"label": "Sources on Data.gouv.fr",
......
[
{
"section": "Sources",
"description": "Les données sanitaires proviennent des sources suivantes :\n- les données issues de la base SI-DEP,\n- les données remontées par les établissements de santé participant à SI-VIC et au réseau OSCOUR®,\n- les données issues de la base Vaccin Covid.\n- les données de la DREES (Direction de la recherche, des études de l'évaluation et des statistiques).\n\nToutes ces données sont agrégées et analysées par Santé Publique France (SpF) et mises à disposition sur Data.gouv.fr",
"description": "Les données sanitaires proviennent des sources suivantes :\n- les données issues de la base SI-DEP,\n- les données remontées par les établissements de santé participant à SI-VIC et au réseau OSCOUR®,\n- les données issues de la base Vaccin Covid.\n- les données de la DREES (Direction de la recherche, des études de l'évaluation et des statistiques).\n\nLes données sur la vaccination et la situation épidémiologique sont agrégées et analysées par Santé Publique France (SpF) et mises à disposition sur data.gouv.fr. Les données de nombre de tests et d’entrées hospitalières selon le statut vaccinal sont produites par la DREES.",
"links": [
{
"label": "Sources sur Data.gouv.fr",
......
......@@ -1179,6 +1179,7 @@
"wallet.autotest.warning": "⚠️ The supervised self-tests are only valid on French territory as part of the \"activity pass\".",
"wallet.expired.pillTitle": "Expired",
"keyFigureChartController.zoomOut": "Zoom out"
"keyFigureChartController.zoomOut": "Zoom out",
"vaccineCompletionController.noWait.explanation.body": "⚠️ En cas de problème pendant la vérification, veuillez présenter le certificat %@ doses."
}
......@@ -1154,6 +1154,7 @@
"wallet.autotest.warning": "⚠️ Les autotests supervisés ne sont valables que sur le territoire français dans le cadre du \"pass activité\".",
"wallet.expired.pillTitle": "Expiré",
"keyFigureChartController.zoomOut": "Dézoomer"
"keyFigureChartController.zoomOut": "Dézoomer",
"vaccineCompletionController.noWait.explanation.body": "⚠️ En cas de problème pendant la vérification, veuillez présenter le certificat %@ doses."
}
......@@ -71,6 +71,7 @@ import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
......@@ -264,7 +265,7 @@ class StopCovid : Application(), LifecycleObserver, RobertApplication, Localized
@OptIn(ExperimentalTime::class)
private fun refreshData() {
appCoroutineScope.launch(Dispatchers.IO) {
launch {
coroutineScope {
launch {
if (firstResume) {
delay(Duration.seconds(1)) // Add some delay to let the main activity start
......@@ -314,7 +315,7 @@ class StopCovid : Application(), LifecycleObserver, RobertApplication, Localized
launch {
robertManager.refreshConfig(this@StopCovid)
}
}.join()
}
injectionContainer.serverManager.okHttpClient.connectionPool.evictAll()
}
......
......@@ -135,7 +135,7 @@ class AppMaintenanceActivity : AppCompatActivity() {
private fun startOpenInStore() {
if (!ConfigConstant.Store.GOOGLE.openInExternalBrowser(this, false)) {
if (!ConfigConstant.Store.HUAWEI.openInExternalBrowser(this, false)) {
ConfigConstant.Store.WEBSITE.openInExternalBrowser(this)
ConfigConstant.Store.TAC_WEBSITE.openInExternalBrowser(this)
}
}
}
......
......@@ -201,44 +201,55 @@ class MainActivity : BaseActivity() {
}
private fun refreshAppBarLayout(destination: NavDestination) {
listOf(
val noAppBarFragments = listOf(
R.id.reportQrCodeFragment,
R.id.venueQrCodeFragment,
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)
windowInsetsController.isAppearanceLightStatusBars =
if (!noAppBarFragment || destination.id == R.id.vaccineCompletionFragment) {
!isNightMode()
} else {
false
}
if (noAppBarFragment) {
// wait for default fragment switch animation time
delay(200L)
binding.appBarLayout.isVisible = false
)
// Fix issue where appBarLayout take space even when gone
val params = binding.navHostFragment.layoutParams as CoordinatorLayout.LayoutParams
params.behavior = null
binding.navHostFragment.requestLayout()
} else {
setSupportActionBar(binding.toolbar)
setupActionBarWithNavController(this@MainActivity, navController)
// wait for default fragment switch animation time
delay(200L)
val forceLightStatusIconFragments = listOf(
R.id.reportQrCodeFragment,
R.id.venueQrCodeFragment,
R.id.walletQRCodeFragment,
R.id.universalQrScanFragment,
R.id.walletQuantityWarningFragment,
)
binding.appBarLayout.isVisible = true
val isNoAppBarFragment = noAppBarFragments.contains(destination.id)
val shouldForceLightStatusIcon = forceLightStatusIconFragments.contains(destination.id)
// Fix issue where appBarLayout take space even when gone
val params = binding.navHostFragment.layoutParams as CoordinatorLayout.LayoutParams
params.behavior = AppBarLayout.ScrollingViewBehavior()
lifecycleScope.launchWhenResumed {
val windowInsetsController = WindowInsetsControllerCompat(window, window.decorView)
windowInsetsController.isAppearanceLightStatusBars =
if (shouldForceLightStatusIcon) {
false
} else {
!isNightMode()
}
if (isNoAppBarFragment) {
// wait for default fragment switch animation time
delay(200L)
binding.appBarLayout.isVisible = false
// Fix issue where appBarLayout take space even when gone
val params = binding.navHostFragment.layoutParams as CoordinatorLayout.LayoutParams
params.behavior = null
binding.navHostFragment.requestLayout()
} else {
setSupportActionBar(binding.toolbar)
setupActionBarWithNavController(this@MainActivity, navController)
// wait for default fragment switch animation time
delay(200L)
binding.appBarLayout.isVisible = true
// Fix issue where appBarLayout take space even when gone
val params = binding.navHostFragment.layoutParams as CoordinatorLayout.LayoutParams
params.behavior = AppBarLayout.ScrollingViewBehavior()
}
}
}
......
......@@ -41,4 +41,5 @@ fun CovidException.getString(strings: Map<String, String>): String = when (this.
ErrorCode.WALLET_CERTIFICATE_UNKNOWN_ERROR -> message
ErrorCode.VENUE_INVALID_FORMAT_EXCEPTION -> strings["enterCodeController.alert.invalidCode.message"] ?: message
ErrorCode.VENUE_EXPIRED_EXCEPTION -> strings["enterCodeController.alert.expiredCode.message"] ?: message
ErrorCode.KEY_FIGURES_NOT_AVAILABLE -> strings["keyFiguresController.fetchError.message"] ?: message
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment