From e0c8399e6879be036fe0bf349b83973c61aeee12 Mon Sep 17 00:00:00 2001 From: Giorgos Papadopoulos Date: Mon, 27 Sep 2021 15:28:30 +0300 Subject: [PATCH 01/16] usd cdn urls for config and public keys requests --- .../java/nl/rijksoverheid/ctr/appconfig/AppConfigModule.kt | 6 ++++-- holder/build.gradle | 1 + .../java/nl/rijksoverheid/ctr/holder/HolderApplication.kt | 2 +- verifier/build.gradle | 1 + .../nl/rijksoverheid/ctr/verifier/VerifierApplication.kt | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/appconfig/src/main/java/nl/rijksoverheid/ctr/appconfig/AppConfigModule.kt b/appconfig/src/main/java/nl/rijksoverheid/ctr/appconfig/AppConfigModule.kt index eba7def4c..270b20f77 100644 --- a/appconfig/src/main/java/nl/rijksoverheid/ctr/appconfig/AppConfigModule.kt +++ b/appconfig/src/main/java/nl/rijksoverheid/ctr/appconfig/AppConfigModule.kt @@ -14,6 +14,8 @@ import nl.rijksoverheid.ctr.appconfig.persistence.* import nl.rijksoverheid.ctr.appconfig.repositories.ConfigRepository import nl.rijksoverheid.ctr.appconfig.repositories.ConfigRepositoryImpl import nl.rijksoverheid.ctr.appconfig.usecases.* +import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient import org.koin.android.ext.koin.androidContext import org.koin.androidx.viewmodel.dsl.viewModel @@ -26,7 +28,7 @@ import retrofit2.Retrofit * @param path Path for the config api, for example "holder" to fetch the config from /holder/config * @param versionCode version code */ -fun appConfigModule(path: String, versionCode: Int) = module { +fun appConfigModule(cdnUrl: String, path: String, versionCode: Int) = module { factory { ConfigRepositoryImpl(get()) } factory { AppConfigUseCaseImpl(get(), get(), get(), get()) } factory { @@ -62,7 +64,7 @@ fun appConfigModule(path: String, versionCode: Int) = module { single { val okHttpClient = get(OkHttpClient::class).newBuilder().build() val retrofit = get(Retrofit::class) - val baseUrl = retrofit.baseUrl().newBuilder().addPathSegments("$path/").build() + val baseUrl = cdnUrl.toHttpUrl().newBuilder().addPathSegments("$path/").build() retrofit.newBuilder().baseUrl(baseUrl).client(okHttpClient).build() .create(AppConfigApi::class.java) } diff --git a/holder/build.gradle b/holder/build.gradle index 2067b7a5c..f7fde3492 100644 --- a/holder/build.gradle +++ b/holder/build.gradle @@ -40,6 +40,7 @@ android { "{" + "\"sha256/lR7gRvqDMW5nhsCMRPE7TKLq0tJkTWMxQ5HAzHCIfQ0=\"" + "}" + buildConfigField "String", "CDN_API_URL", "\"https://holder-api-cdn.coronacheck.nl/v5/\"" manifestPlaceholders = [appLabel: "@string/app_name", deepLinkHost: "coronacheck.nl", digidSchema: "coronacheck"] javaCompileOptions { diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/HolderApplication.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/HolderApplication.kt index d57e04cac..86409a503 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/HolderApplication.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/HolderApplication.kt @@ -77,7 +77,7 @@ open class HolderApplication : SharedApplication(), Configuration.Provider { BuildConfig.CERTIFICATE_PINS, ), sharedModule, - appConfigModule("holder", BuildConfig.VERSION_CODE), + appConfigModule(BuildConfig.CDN_API_URL,"holder", BuildConfig.VERSION_CODE), introductionModule, *getAdditionalModules().toTypedArray(), designModule diff --git a/verifier/build.gradle b/verifier/build.gradle index 84a3e7f58..415608c07 100644 --- a/verifier/build.gradle +++ b/verifier/build.gradle @@ -37,6 +37,7 @@ android { "{" + "\"sha256/lR7gRvqDMW5nhsCMRPE7TKLq0tJkTWMxQ5HAzHCIfQ0=\"" + "}" + buildConfigField "String", "CDN_API_URL", "\"https://verifier-api-cdn.coronacheck.nl/v5/\"" } signingConfigs { diff --git a/verifier/src/main/java/nl/rijksoverheid/ctr/verifier/VerifierApplication.kt b/verifier/src/main/java/nl/rijksoverheid/ctr/verifier/VerifierApplication.kt index 1fbcc160d..df6e758a4 100644 --- a/verifier/src/main/java/nl/rijksoverheid/ctr/verifier/VerifierApplication.kt +++ b/verifier/src/main/java/nl/rijksoverheid/ctr/verifier/VerifierApplication.kt @@ -44,7 +44,7 @@ open class VerifierApplication : SharedApplication() { verifierModule("verifier"), verifierIntroductionModule, sharedModule, - appConfigModule("verifier", BuildConfig.VERSION_CODE), + appConfigModule(BuildConfig.CDN_API_URL, "verifier", BuildConfig.VERSION_CODE), introductionModule, *getAdditionalModules().toTypedArray(), designModule, From 8a90d89b904f236e6302478ef76b7614db599b5d Mon Sep 17 00:00:00 2001 From: Giorgos Papadopoulos Date: Mon, 27 Sep 2021 17:10:15 +0300 Subject: [PATCH 02/16] 1968 remove retry button on create QR and strippen refresh --- .../java/nl/rijksoverheid/ctr/holder/BaseFragment.kt | 6 +++++- .../database/usecases/GetRemoteGreenCardsUseCase.kt | 2 +- .../ctr/holder/ui/create_qr/YourEventsFragment.kt | 10 ++++++++-- .../ui/create_qr/repositories/CoronaCheckRepository.kt | 1 - .../main/res/layout/item_my_overview_green_card.xml | 2 +- holder/src/main/res/values-en/strings.xml | 2 +- holder/src/main/res/values/strings.xml | 2 +- 7 files changed, 17 insertions(+), 8 deletions(-) diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/BaseFragment.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/BaseFragment.kt index c55e75c87..5752b7013 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/BaseFragment.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/BaseFragment.kt @@ -21,6 +21,10 @@ abstract class BaseFragment(contentLayoutId: Int) : Fragment(contentLayoutId) { */ abstract fun onButtonClickWithRetryAction() + open fun onButtonClickWithRetryTitle(): Int { + return R.string.dialog_retry + } + /** * Get the current [Flow] for this screen */ @@ -33,7 +37,7 @@ abstract class BaseFragment(contentLayoutId: Int) : Fragment(contentLayoutId) { context = requireContext(), title = R.string.dialog_no_internet_connection_title, message = getString(R.string.dialog_no_internet_connection_description), - positiveButtonText = R.string.dialog_retry, + positiveButtonText = onButtonClickWithRetryTitle(), positiveButtonCallback = { onButtonClickWithRetryAction() }, diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/usecases/GetRemoteGreenCardsUseCase.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/usecases/GetRemoteGreenCardsUseCase.kt index b6a681ee7..4eccf1628 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/usecases/GetRemoteGreenCardsUseCase.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/usecases/GetRemoteGreenCardsUseCase.kt @@ -30,7 +30,7 @@ class GetRemoteGreenCardsUseCaseImpl( prepareIssueResult.response } is NetworkRequestResult.Failed -> { - return RemoteGreenCardsResult.Error(prepareIssueResult) + return RemoteGreenCardsResult.Error(NetworkRequestResult.Failed.ClientNetworkError(HolderStep.PrepareIssueNetworkRequest)) } } diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragment.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragment.kt index ae69b1c03..7a5c3f917 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragment.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragment.kt @@ -57,9 +57,15 @@ class YourEventsFragment : BaseFragment(R.layout.fragment_your_events) { private val yourEventsViewModel: YourEventsViewModel by viewModel() - private val configProvidersUseCase: ConfigProvidersUseCase by inject() + override fun onButtonClickWithRetryTitle(): Int { + return R.string.my_overview + } override fun onButtonClickWithRetryAction() { + navigateSafety(YourEventsFragmentDirections.actionMyOverview()) + } + + private fun retrieveEvents() { when (val type = args.type) { is YourEventsFragmentType.TestResult2 -> { yourEventsViewModel.saveNegativeTest2( @@ -547,7 +553,7 @@ class YourEventsFragment : BaseFragment(R.layout.fragment_your_events) { private fun handleButton(binding: FragmentYourEventsBinding) { binding.bottom.setButtonClick { - onButtonClickWithRetryAction() + retrieveEvents() } } diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/repositories/CoronaCheckRepository.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/repositories/CoronaCheckRepository.kt index 0b72ced4b..a45714fa0 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/repositories/CoronaCheckRepository.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/repositories/CoronaCheckRepository.kt @@ -1,6 +1,5 @@ package nl.rijksoverheid.ctr.holder.ui.create_qr.repositories -import android.net.NetworkRequest import android.util.Base64 import nl.rijksoverheid.ctr.api.factory.NetworkRequestResultFactory import nl.rijksoverheid.ctr.shared.models.NetworkRequestResult diff --git a/holder/src/main/res/layout/item_my_overview_green_card.xml b/holder/src/main/res/layout/item_my_overview_green_card.xml index 33320b4f6..b11eb716a 100644 --- a/holder/src/main/res/layout/item_my_overview_green_card.xml +++ b/holder/src/main/res/layout/item_my_overview_green_card.xml @@ -28,7 +28,6 @@ android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" - tools:src="@drawable/illustration_hand_qr_nl" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -164,6 +163,7 @@ app:htmlText="@string/my_overview_green_card_server_error" app:layout_constraintTop_toBottomOf="@+id/test_result" app:layout_constraintStart_toEndOf="@id/error_icon" + app:layout_constraintEnd_toEndOf="parent" android:visibility="invisible" app:layout_goneMarginTop="32dp" tools:visibility="visible" /> diff --git a/holder/src/main/res/values-en/strings.xml b/holder/src/main/res/values-en/strings.xml index 4ac448652..b4741d75a 100644 --- a/holder/src/main/res/values-en/strings.xml +++ b/holder/src/main/res/values-en/strings.xml @@ -369,7 +369,7 @@ About your recovery certificate
You’ve only got an international QR code.]]>

The reason is that the type of test is not accepted. An international recovery certificate can only be made with a PCR-test.]]>
- Try again]]> + Try again]]> Vaccination certificate corrected diff --git a/holder/src/main/res/values/strings.xml b/holder/src/main/res/values/strings.xml index 56191f6a1..a6f94f0e8 100644 --- a/holder/src/main/res/values/strings.xml +++ b/holder/src/main/res/values/strings.xml @@ -369,7 +369,7 @@ Over je herstelbewijs
Dit komt doordat het type test niet voldoet in Nederland.]]>

Dit komt doordat het type test niet voldoet. Alleen van een PCR-test kan een internationaal bewijs gemaakt worden.]]>
- Probeer opnieuw]]> + Probeer opnieuw]]> Vaccinatiebewijs gecorrigeerd From 2c0049866053bb36624c5aa73ad85d108da5ce6b Mon Sep 17 00:00:00 2001 From: Giorgos Papadopoulos Date: Mon, 27 Sep 2021 17:15:44 +0300 Subject: [PATCH 03/16] add acceptance urls --- holder/build.gradle | 1 + .../persistence/database/usecases/GetRemoteGreenCardsUseCase.kt | 2 +- verifier/build.gradle | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/holder/build.gradle b/holder/build.gradle index f7fde3492..e65b66ff5 100644 --- a/holder/build.gradle +++ b/holder/build.gradle @@ -97,6 +97,7 @@ android { "{" + "\"sha256/lR7gRvqDMW5nhsCMRPE7TKLq0tJkTWMxQ5HAzHCIfQ0=\"" + "}" + buildConfigField "String", "CDN_API_URL", "\"https://holder-api-cdn.acc.coronacheck.nl/v5/\"" dimension "environment" versionNameSuffix "-acc" applicationIdSuffix ".acc" diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/usecases/GetRemoteGreenCardsUseCase.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/usecases/GetRemoteGreenCardsUseCase.kt index 4eccf1628..b6a681ee7 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/usecases/GetRemoteGreenCardsUseCase.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/usecases/GetRemoteGreenCardsUseCase.kt @@ -30,7 +30,7 @@ class GetRemoteGreenCardsUseCaseImpl( prepareIssueResult.response } is NetworkRequestResult.Failed -> { - return RemoteGreenCardsResult.Error(NetworkRequestResult.Failed.ClientNetworkError(HolderStep.PrepareIssueNetworkRequest)) + return RemoteGreenCardsResult.Error(prepareIssueResult) } } diff --git a/verifier/build.gradle b/verifier/build.gradle index 415608c07..bc72db6e1 100644 --- a/verifier/build.gradle +++ b/verifier/build.gradle @@ -84,6 +84,7 @@ android { "{" + "\"sha256/lR7gRvqDMW5nhsCMRPE7TKLq0tJkTWMxQ5HAzHCIfQ0=\"" + "}" + buildConfigField "String", "CDN_API_URL", "\"https://verifier-api-cdn.acc.coronacheck.nl/v5/\"" apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.firebase.crashlytics' } From da922e9342a44866aa2169e627bbb9b1283fe9ab Mon Sep 17 00:00:00 2001 From: Giorgos Papadopoulos Date: Mon, 27 Sep 2021 17:46:51 +0300 Subject: [PATCH 04/16] fix tests --- .../ctr/holder/ui/create_qr/YourEventsFragment.kt | 5 ++--- .../ctr/holder/CertificatePinTest.kt | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragment.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragment.kt index 7a5c3f917..4779f9831 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragment.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragment.kt @@ -26,7 +26,6 @@ import nl.rijksoverheid.ctr.holder.persistence.database.DatabaseSyncerResult import nl.rijksoverheid.ctr.holder.persistence.database.entities.OriginType import nl.rijksoverheid.ctr.holder.ui.create_qr.items.YourEventWidget import nl.rijksoverheid.ctr.holder.ui.create_qr.models.* -import nl.rijksoverheid.ctr.holder.ui.create_qr.usecases.ConfigProvidersUseCase import nl.rijksoverheid.ctr.holder.ui.create_qr.util.InfoScreenUtil import nl.rijksoverheid.ctr.holder.ui.create_qr.util.RemoteEventUtil import nl.rijksoverheid.ctr.holder.ui.create_qr.util.RemoteProtocol3Util @@ -65,7 +64,7 @@ class YourEventsFragment : BaseFragment(R.layout.fragment_your_events) { navigateSafety(YourEventsFragmentDirections.actionMyOverview()) } - private fun retrieveEvents() { + private fun retrieveGreenCards() { when (val type = args.type) { is YourEventsFragmentType.TestResult2 -> { yourEventsViewModel.saveNegativeTest2( @@ -553,7 +552,7 @@ class YourEventsFragment : BaseFragment(R.layout.fragment_your_events) { private fun handleButton(binding: FragmentYourEventsBinding) { binding.bottom.setButtonClick { - retrieveEvents() + retrieveGreenCards() } } diff --git a/holder/src/test/java/nl/rijksoverheid/ctr/holder/CertificatePinTest.kt b/holder/src/test/java/nl/rijksoverheid/ctr/holder/CertificatePinTest.kt index f1bede9d7..53bf3511d 100644 --- a/holder/src/test/java/nl/rijksoverheid/ctr/holder/CertificatePinTest.kt +++ b/holder/src/test/java/nl/rijksoverheid/ctr/holder/CertificatePinTest.kt @@ -3,10 +3,19 @@ package nl.rijksoverheid.ctr.holder import com.squareup.moshi.Moshi import kotlinx.coroutines.runBlocking import nl.rijksoverheid.ctr.api.json.JsonObjectJsonAdapter +import nl.rijksoverheid.ctr.appconfig.AppConfigViewModel +import nl.rijksoverheid.ctr.appconfig.AppConfigViewModelImpl import nl.rijksoverheid.ctr.appconfig.api.AppConfigApi +import nl.rijksoverheid.ctr.appconfig.appConfigModule +import nl.rijksoverheid.ctr.appconfig.isVerifierApp +import nl.rijksoverheid.ctr.appconfig.persistence.* +import nl.rijksoverheid.ctr.appconfig.repositories.ConfigRepository +import nl.rijksoverheid.ctr.appconfig.repositories.ConfigRepositoryImpl +import nl.rijksoverheid.ctr.appconfig.usecases.* import okhttp3.CertificatePinner import okhttp3.CertificatePinner.Companion.sha256Hash import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer @@ -15,7 +24,10 @@ import okhttp3.tls.HeldCertificate import org.junit.Assert.assertThrows import org.junit.Test import org.junit.runner.RunWith +import org.koin.android.ext.koin.androidContext +import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.core.context.loadKoinModules +import org.koin.core.context.unloadKoinModules import org.koin.dsl.module import org.koin.test.AutoCloseKoinTest import org.koin.test.get @@ -57,7 +69,8 @@ class CertificatePinTest: AutoCloseKoinTest() { setBody("{}") }) - loadKoinModules(apiModule(server.url("/"))) + unloadKoinModules(appConfigModule(BuildConfig.CDN_API_URL, "holder", BuildConfig.VERSION_CODE)) + loadKoinModules(listOf(apiModule(server.url("/")), appConfigModule(server.url("/").toString(), "holder", BuildConfig.VERSION_CODE))) val configApi: AppConfigApi = get() From 098c577759bd2ea2894053cebdc437e8eb4fb961 Mon Sep 17 00:00:00 2001 From: Giorgos Papadopoulos Date: Mon, 27 Sep 2021 17:52:09 +0300 Subject: [PATCH 05/16] bump version --- holder/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/holder/build.gradle b/holder/build.gradle index e65b66ff5..322841934 100644 --- a/holder/build.gradle +++ b/holder/build.gradle @@ -7,7 +7,7 @@ plugins { } def appVersionCode = System.getenv("GITHUB_RUN_NUMBER") != null ? System.getenv("GITHUB_RUN_NUMBER").toInteger() : 1000000 -version = "2.3.2" +version = "2.3.3" archivesBaseName = "holder-v${version}-${appVersionCode}" android { From c1b7d490601cac12335ac866727fad76ecf0de31 Mon Sep 17 00:00:00 2001 From: Bart Nijland Date: Tue, 28 Sep 2021 11:49:01 +0200 Subject: [PATCH 06/16] Set CommercialTest flow step when coming from commercial test --- .../ctr/holder/ui/create_qr/CommercialTestCodeFragment.kt | 5 +++-- .../ctr/holder/ui/create_qr/YourEventsFragment.kt | 6 +++++- .../ctr/holder/ui/create_qr/YourEventsFragmentType.kt | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/CommercialTestCodeFragment.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/CommercialTestCodeFragment.kt index b47f9056e..17508ad45 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/CommercialTestCodeFragment.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/CommercialTestCodeFragment.kt @@ -219,9 +219,10 @@ import org.koin.androidx.viewmodel.scope.emptyState CommercialTestCodeFragmentDirections.actionYourEvents( type = YourEventsFragmentType.RemoteProtocol3Type( mapOf(result.remoteTestResult to result.signedResponseWithTestResult.rawResponse), - originType = OriginType.Test + originType = OriginType.Test, + fromCommercialTestCode = true ), - toolbarTitle = getString(R.string.commercial_test_type_title) + toolbarTitle = getString(R.string.commercial_test_type_title), ) ) } diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragment.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragment.kt index 4779f9831..1de546981 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragment.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragment.kt @@ -96,7 +96,11 @@ class YourEventsFragment : BaseFragment(R.layout.fragment_your_events) { is YourEventsFragmentType.RemoteProtocol3Type -> { return when (type.originType) { is OriginType.Test -> { - HolderFlow.DigidTest + if (type.fromCommercialTestCode) { + HolderFlow.CommercialTest + } else { + HolderFlow.DigidTest + } } is OriginType.Recovery -> { HolderFlow.Recovery diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragmentType.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragmentType.kt index 7e50eeab4..c9efd2398 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragmentType.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/YourEventsFragmentType.kt @@ -43,6 +43,7 @@ sealed class YourEventsFragmentType : Parcelable { val remoteEvents: Map, val originType: OriginType, val eventProviders: List = emptyList(), + val fromCommercialTestCode: Boolean = false ) : YourEventsFragmentType(), Parcelable @Parcelize From 66dc8e04357445b1816ae92248a76e40bc8d8775 Mon Sep 17 00:00:00 2001 From: Giorgos Papadopoulos Date: Tue, 28 Sep 2021 18:53:01 +0300 Subject: [PATCH 07/16] strippen error new logic decision implementation --- .../ui/create_qr/models/DashboardItems.kt | 15 +++++++++- .../ui/myoverview/DashboardViewModel.kt | 28 +++++++++++++++++++ .../ui/myoverview/MyOverviewFragment.kt | 1 + .../ui/myoverview/MyOverviewTabsFragment.kt | 2 +- .../items/MyOverviewGreenCardAdapterItem.kt | 28 ++++++++++++------- .../layout/item_my_overview_green_card.xml | 11 ++++---- holder/src/main/res/values-en/strings.xml | 4 +-- holder/src/main/res/values/strings.xml | 4 +-- 8 files changed, 72 insertions(+), 21 deletions(-) diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/models/DashboardItems.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/models/DashboardItems.kt index 1b2c9540e..59f372394 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/models/DashboardItems.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/create_qr/models/DashboardItems.kt @@ -1,6 +1,19 @@ +/* + * Copyright (c) 2021 De Staat der Nederlanden, Ministerie van Volksgezondheid, Welzijn en Sport. + * Licensed under the EUROPEAN UNION PUBLIC LICENCE v. 1.2 + * + * SPDX-License-Identifier: EUPL-1.2 + * + */ package nl.rijksoverheid.ctr.holder.ui.create_qr.models +sealed class DashboardErrorState { + object None: DashboardErrorState() + object RetryErrorState: DashboardErrorState() + object HelpdeskErrorState: DashboardErrorState() +} + data class DashboardItems( val domesticItems: List, val internationalItems: List -) \ No newline at end of file +) diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt index 963ee739c..2c1a824d5 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt @@ -13,17 +13,25 @@ import nl.rijksoverheid.ctr.holder.persistence.database.HolderDatabase import nl.rijksoverheid.ctr.holder.persistence.database.HolderDatabaseSyncer import nl.rijksoverheid.ctr.holder.persistence.database.entities.GreenCardType import nl.rijksoverheid.ctr.holder.persistence.database.models.GreenCard +import nl.rijksoverheid.ctr.holder.ui.create_qr.models.DashboardErrorState import nl.rijksoverheid.ctr.holder.ui.create_qr.usecases.GetDashboardItemsUseCase import nl.rijksoverheid.ctr.holder.ui.create_qr.util.GreenCardRefreshUtil import nl.rijksoverheid.ctr.holder.ui.myoverview.models.DashboardTabItem import nl.rijksoverheid.ctr.shared.livedata.Event +import java.time.OffsetDateTime abstract class DashboardViewModel : ViewModel() { open val dashboardTabItemsLiveData: LiveData> = MutableLiveData() open val databaseSyncerResultLiveData: LiveData> = MutableLiveData() + var dashboardErrorState: DashboardErrorState = DashboardErrorState.None + abstract fun refresh(forceSync: Boolean = false) abstract fun removeGreenCard(greenCard: GreenCard) + + companion object { + internal const val retryIntervalMinutes = 10L + } } class DashboardViewModelImpl( @@ -35,6 +43,8 @@ class DashboardViewModelImpl( private val mutex = Mutex() + private var lastRetryUpdate: OffsetDateTime? = null + override fun refresh(forceSync: Boolean) { viewModelScope.launch { mutex.withLock { @@ -55,6 +65,19 @@ class DashboardViewModelImpl( syncWithRemote = shouldLoadNewCredentials ) + if (shouldLoadNewCredentials && databaseSyncerResult is DatabaseSyncerResult.Failed.ServerError) { + dashboardErrorState = if (dashboardErrorState == DashboardErrorState.RetryErrorState || !shouldAllowRetry()) { + lastRetryUpdate = OffsetDateTime.now() + DashboardErrorState.RetryErrorState + } else { + DashboardErrorState.HelpdeskErrorState + } + } + + if (databaseSyncerResult !is DatabaseSyncerResult.Failed.ServerError) { + dashboardErrorState = DashboardErrorState.None + } + (databaseSyncerResultLiveData as MutableLiveData).postValue( Event(databaseSyncerResult) ) @@ -71,6 +94,11 @@ class DashboardViewModelImpl( } } + private fun shouldAllowRetry(): Boolean { + val lastUpdate = lastRetryUpdate ?: return true + return OffsetDateTime.now().isAfter(lastUpdate.plusMinutes(retryIntervalMinutes)) + } + override fun removeGreenCard(greenCard: GreenCard) { viewModelScope.launch { holderDatabase.greenCardDao().delete(greenCard.greenCardEntity) diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt index 8355ea399..c5b686e4d 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt @@ -143,6 +143,7 @@ class MyOverviewFragment : Fragment(R.layout.fragment_my_overview) { forceSync = true ) }, + errorState = dashboardViewModel.dashboardErrorState, ) ) } diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewTabsFragment.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewTabsFragment.kt index 92f9985d4..ccc96799a 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewTabsFragment.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewTabsFragment.kt @@ -129,7 +129,7 @@ class MyOverviewTabsFragment : Fragment(R.layout.fragment_tabs_my_overview) { }, negativeButtonText = R.string.dialog_close, ) - } else { + } else if (it !is DatabaseSyncerResult.Failed.ServerError) { dialogUtil.presentDialog( context = requireContext(), title = R.string.dialog_title_no_internet, diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt index d94e37bb9..d8620e58c 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt @@ -11,7 +11,6 @@ package nl.rijksoverheid.ctr.holder.ui.myoverview.items import android.view.View import androidx.core.content.ContextCompat import com.xwray.groupie.viewbinding.BindableItem -import nl.rijksoverheid.ctr.design.ext.enableCustomLinks import nl.rijksoverheid.ctr.design.ext.getThemeColor import nl.rijksoverheid.ctr.holder.R import nl.rijksoverheid.ctr.holder.databinding.ItemMyOverviewGreenCardBinding @@ -19,8 +18,8 @@ import nl.rijksoverheid.ctr.holder.persistence.database.DatabaseSyncerResult import nl.rijksoverheid.ctr.holder.persistence.database.entities.CredentialEntity import nl.rijksoverheid.ctr.holder.persistence.database.entities.GreenCardType import nl.rijksoverheid.ctr.holder.persistence.database.models.GreenCard +import nl.rijksoverheid.ctr.holder.ui.create_qr.models.DashboardErrorState import nl.rijksoverheid.ctr.holder.ui.create_qr.models.DashboardItem -import nl.rijksoverheid.ctr.holder.ui.create_qr.usecases.MyOverviewItem import nl.rijksoverheid.ctr.holder.ui.create_qr.util.OriginState import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -32,6 +31,7 @@ class MyOverviewGreenCardAdapterItem( private val databaseSyncerResult: DatabaseSyncerResult = DatabaseSyncerResult.Success, private val onButtonClick: (greenCard: GreenCard, credential: CredentialEntity) -> Unit, private val onRetryClick: () -> Unit = {}, + private val errorState: DashboardErrorState = DashboardErrorState.None, ) : BindableItem(R.layout.item_my_overview_green_card.toLong()), KoinComponent { @@ -99,7 +99,6 @@ class MyOverviewGreenCardAdapterItem( viewBinding.proof2Subtitle.setTextColor(context.getThemeColor(android.R.attr.textColorPrimary)) viewBinding.proof3Subtitle.setTextColor(context.getThemeColor(android.R.attr.textColorPrimary)) viewBinding.errorText.setHtmlText("") - viewBinding.errorTextRetry.setHtmlText("") viewBinding.errorIcon.visibility = View.GONE viewBinding.errorText.visibility = View.GONE viewBinding.errorTextRetry.visibility = View.GONE @@ -111,22 +110,31 @@ class MyOverviewGreenCardAdapterItem( private fun showError(viewBinding: ItemMyOverviewGreenCardBinding) { if (credentialState is DashboardItem.GreenCardItem.CredentialState.NoCredential) { - val context = viewBinding.errorText.context when (databaseSyncerResult) { is DatabaseSyncerResult.Failed.NetworkError -> { viewBinding.errorText.setHtmlText(R.string.my_overview_green_card_internet_error) viewBinding.errorText.enableCustomLinks(onRetryClick) - viewBinding.errorTextRetry.setHtmlText("") viewBinding.errorIcon.visibility = View.VISIBLE viewBinding.errorText.visibility = View.VISIBLE viewBinding.errorTextRetry.visibility = View.GONE } is DatabaseSyncerResult.Failed.ServerError -> { - viewBinding.errorText.setHtmlText(R.string.my_overview_green_card_server_error) - viewBinding.errorText.enableCustomLinks(onRetryClick) - viewBinding.errorIcon.visibility = View.VISIBLE - viewBinding.errorText.visibility = View.VISIBLE - viewBinding.errorTextRetry.visibility = View.GONE + when (errorState) { + DashboardErrorState.HelpdeskErrorState -> { + viewBinding.errorText.visibility = View.GONE + viewBinding.errorTextRetry.visibility = View.VISIBLE + } + DashboardErrorState.None -> { + viewBinding.errorText.visibility = View.GONE + viewBinding.errorTextRetry.visibility = View.GONE + } + DashboardErrorState.RetryErrorState -> { + viewBinding.errorText.setHtmlText(R.string.my_overview_green_card_server_error) + viewBinding.errorText.enableCustomLinks(onRetryClick) + viewBinding.errorText.visibility = View.VISIBLE + viewBinding.errorTextRetry.visibility = View.GONE + } + } } else -> { } diff --git a/holder/src/main/res/layout/item_my_overview_green_card.xml b/holder/src/main/res/layout/item_my_overview_green_card.xml index b11eb716a..435fb603e 100644 --- a/holder/src/main/res/layout/item_my_overview_green_card.xml +++ b/holder/src/main/res/layout/item_my_overview_green_card.xml @@ -153,8 +153,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="@style/App.TextAppearance.MaterialComponents.Body2.Secondary" - android:layout_marginStart="2dp" - android:layout_marginEnd="32dp" android:layout_marginTop="16dp" android:layout_marginBottom="16dp" android:textAlignment="textStart" @@ -162,8 +160,9 @@ android:textColorLink="@color/error" app:htmlText="@string/my_overview_green_card_server_error" app:layout_constraintTop_toBottomOf="@+id/test_result" - app:layout_constraintStart_toEndOf="@id/error_icon" - app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="@id/test_result" + app:layout_constraintEnd_toEndOf="@+id/test_result" + android:paddingHorizontal="16dp" android:visibility="invisible" app:layout_goneMarginTop="32dp" tools:visibility="visible" /> @@ -180,7 +179,9 @@ android:textColorLink="?android:textColorPrimary" app:htmlText="@string/my_overview_green_card_server_error_after_retry" app:layout_constraintTop_toBottomOf="@+id/error_text" - app:layout_constraintStart_toStartOf="@id/error_text" + app:layout_constraintStart_toStartOf="@+id/test_result" + app:layout_constraintEnd_toEndOf="@+id/test_result" + android:paddingHorizontal="16dp" android:visibility="invisible" app:layout_goneMarginTop="32dp" tools:visibility="visible" /> diff --git a/holder/src/main/res/values-en/strings.xml b/holder/src/main/res/values-en/strings.xml index b4741d75a..58b07b5f8 100644 --- a/holder/src/main/res/values-en/strings.xml +++ b/holder/src/main/res/values-en/strings.xml @@ -369,8 +369,8 @@ About your recovery certificate
You’ve only got an international QR code.]]>

The reason is that the type of test is not accepted. An international recovery certificate can only be made with a PCR-test.]]>
- - + Try again]]> + No success? Please call the help desk at 0800-1421 (free)]]> Try again]]> Vaccination certificate corrected You’ve wrongly received a Dutch vaccination certificate due to a technical error. This was corrected. diff --git a/holder/src/main/res/values/strings.xml b/holder/src/main/res/values/strings.xml index a6f94f0e8..eb25f238e 100644 --- a/holder/src/main/res/values/strings.xml +++ b/holder/src/main/res/values/strings.xml @@ -369,8 +369,8 @@ Over je herstelbewijs
Dit komt doordat het type test niet voldoet in Nederland.]]>

Dit komt doordat het type test niet voldoet. Alleen van een PCR-test kan een internationaal bewijs gemaakt worden.]]>
- - + Probeer opnieuw]]> + Helpt dat niet? Bel dan de helpdesk op 0800-1421 (gratis)]]> Probeer opnieuw]]> Vaccinatiebewijs gecorrigeerd Je hebt een Nederlands vaccinatiebewijs gekregen door een fout. Dit is nu gecorrigeerd. From ad0a3d489612ce7f817eaf2b5e9669375f0422df Mon Sep 17 00:00:00 2001 From: Bart Nijland Date: Wed, 29 Sep 2021 10:44:46 +0200 Subject: [PATCH 08/16] Do not refresh credentials when syncing time --- .../ui/myoverview/DashboardViewModel.kt | 30 +++++++++---------- .../ui/myoverview/MyOverviewFragment.kt | 3 +- .../ui/myoverview/MyOverviewTabsFragment.kt | 13 ++++---- .../myoverview/models/DashboardSyncState.kt | 7 +++++ 4 files changed, 31 insertions(+), 22 deletions(-) create mode 100644 holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/models/DashboardSyncState.kt diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt index 2c1a824d5..b412b1a8d 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt @@ -16,6 +16,7 @@ import nl.rijksoverheid.ctr.holder.persistence.database.models.GreenCard import nl.rijksoverheid.ctr.holder.ui.create_qr.models.DashboardErrorState import nl.rijksoverheid.ctr.holder.ui.create_qr.usecases.GetDashboardItemsUseCase import nl.rijksoverheid.ctr.holder.ui.create_qr.util.GreenCardRefreshUtil +import nl.rijksoverheid.ctr.holder.ui.myoverview.models.DashboardSync import nl.rijksoverheid.ctr.holder.ui.myoverview.models.DashboardTabItem import nl.rijksoverheid.ctr.shared.livedata.Event import java.time.OffsetDateTime @@ -26,7 +27,7 @@ abstract class DashboardViewModel : ViewModel() { var dashboardErrorState: DashboardErrorState = DashboardErrorState.None - abstract fun refresh(forceSync: Boolean = false) + abstract fun refresh(dashboardSync: DashboardSync = DashboardSync.CheckCredentialsExpired) abstract fun removeGreenCard(greenCard: GreenCard) companion object { @@ -45,12 +46,22 @@ class DashboardViewModelImpl( private var lastRetryUpdate: OffsetDateTime? = null - override fun refresh(forceSync: Boolean) { + override fun refresh(dashboardSync: DashboardSync) { viewModelScope.launch { mutex.withLock { // Check if we need to refresh our data val hasDoneRefreshCall = databaseSyncerResultLiveData.value?.peekContent() != null - val shouldLoadNewCredentials = (forceSync) || (greenCardRefreshUtil.shouldRefresh() && !hasDoneRefreshCall) + val shouldLoadNewCredentials = when (dashboardSync) { + is DashboardSync.ForceSync -> { + true + } + is DashboardSync.DisableSync -> { + false + } + is DashboardSync.CheckCredentialsExpired -> { + (greenCardRefreshUtil.shouldRefresh() && !hasDoneRefreshCall) + } + } val allGreenCards = holderDatabase.greenCardDao().getAll() @@ -65,19 +76,6 @@ class DashboardViewModelImpl( syncWithRemote = shouldLoadNewCredentials ) - if (shouldLoadNewCredentials && databaseSyncerResult is DatabaseSyncerResult.Failed.ServerError) { - dashboardErrorState = if (dashboardErrorState == DashboardErrorState.RetryErrorState || !shouldAllowRetry()) { - lastRetryUpdate = OffsetDateTime.now() - DashboardErrorState.RetryErrorState - } else { - DashboardErrorState.HelpdeskErrorState - } - } - - if (databaseSyncerResult !is DatabaseSyncerResult.Failed.ServerError) { - dashboardErrorState = DashboardErrorState.None - } - (databaseSyncerResultLiveData as MutableLiveData).postValue( Event(databaseSyncerResult) ) diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt index c5b686e4d..3d6044c94 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewFragment.kt @@ -15,6 +15,7 @@ import nl.rijksoverheid.ctr.holder.persistence.database.entities.GreenCardType import nl.rijksoverheid.ctr.holder.persistence.database.entities.OriginType import nl.rijksoverheid.ctr.holder.ui.create_qr.models.DashboardItem import nl.rijksoverheid.ctr.holder.ui.myoverview.items.* +import nl.rijksoverheid.ctr.holder.ui.myoverview.models.DashboardSync import nl.rijksoverheid.ctr.holder.ui.myoverview.models.QrCodeFragmentData import nl.rijksoverheid.ctr.shared.ext.navigateSafety import nl.rijksoverheid.ctr.shared.ext.sharedViewModelWithOwner @@ -140,7 +141,7 @@ class MyOverviewFragment : Fragment(R.layout.fragment_my_overview) { }, onRetryClick = { dashboardViewModel.refresh( - forceSync = true + dashboardSync = DashboardSync.ForceSync ) }, errorState = dashboardViewModel.dashboardErrorState, diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewTabsFragment.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewTabsFragment.kt index ccc96799a..018831361 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewTabsFragment.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewTabsFragment.kt @@ -21,6 +21,7 @@ import nl.rijksoverheid.ctr.holder.R import nl.rijksoverheid.ctr.holder.databinding.FragmentTabsMyOverviewBinding import nl.rijksoverheid.ctr.holder.persistence.PersistenceManager import nl.rijksoverheid.ctr.holder.persistence.database.DatabaseSyncerResult +import nl.rijksoverheid.ctr.holder.ui.myoverview.models.DashboardSync import nl.rijksoverheid.ctr.holder.ui.myoverview.models.DashboardTabItem import nl.rijksoverheid.ctr.shared.ext.navigateSafety import nl.rijksoverheid.ctr.shared.livedata.EventObserver @@ -82,7 +83,9 @@ class MyOverviewTabsFragment : Fragment(R.layout.fragment_tabs_my_overview) { */ private fun observeServerTimeSynced() { clockDeviationUseCase.serverTimeSyncedLiveData.observe(viewLifecycleOwner, EventObserver { - dashboardViewModel.refresh() + dashboardViewModel.refresh( + dashboardSync = DashboardSync.DisableSync + ) }) } @@ -124,7 +127,7 @@ class MyOverviewTabsFragment : Fragment(R.layout.fragment_tabs_my_overview) { positiveButtonText = R.string.app_status_internet_required_action, positiveButtonCallback = { refresh( - forceSync = true + dashboardSync = DashboardSync.ForceSync ) }, negativeButtonText = R.string.dialog_close, @@ -137,7 +140,7 @@ class MyOverviewTabsFragment : Fragment(R.layout.fragment_tabs_my_overview) { positiveButtonText = R.string.app_status_internet_required_action, positiveButtonCallback = { refresh( - forceSync = true + dashboardSync = DashboardSync.ForceSync ) }, negativeButtonText = R.string.dialog_close, @@ -148,8 +151,8 @@ class MyOverviewTabsFragment : Fragment(R.layout.fragment_tabs_my_overview) { ) } - private fun refresh(forceSync: Boolean = false) { - dashboardViewModel.refresh(forceSync) + private fun refresh(dashboardSync: DashboardSync = DashboardSync.CheckCredentialsExpired) { + dashboardViewModel.refresh(dashboardSync) refreshHandler.postDelayed( refreshRunnable, TimeUnit.SECONDS.toMillis(60) diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/models/DashboardSyncState.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/models/DashboardSyncState.kt new file mode 100644 index 000000000..85318796e --- /dev/null +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/models/DashboardSyncState.kt @@ -0,0 +1,7 @@ +package nl.rijksoverheid.ctr.holder.ui.myoverview.models + +sealed class DashboardSync { + object ForceSync: DashboardSync() + object DisableSync: DashboardSync() + object CheckCredentialsExpired: DashboardSync() +} \ No newline at end of file From 8435e005a669bf90a0ddc557107068388b54fdba Mon Sep 17 00:00:00 2001 From: Bart Nijland Date: Wed, 29 Sep 2021 11:22:14 +0200 Subject: [PATCH 09/16] Fix styling of error text in green card item --- .../ctr/design/ext/ContextExt.kt | 10 ++++- .../ctr/design/views/HtmlTextViewWidget.kt | 17 +++++++- .../views/OutlinedToggleButtonWidget.kt | 4 +- design/src/main/res/values/attrs.xml | 2 + .../items/MyOverviewGreenCardAdapterItem.kt | 39 ++++++++----------- .../layout/item_my_overview_green_card.xml | 29 +++----------- 6 files changed, 50 insertions(+), 51 deletions(-) diff --git a/design/src/main/java/nl/rijksoverheid/ctr/design/ext/ContextExt.kt b/design/src/main/java/nl/rijksoverheid/ctr/design/ext/ContextExt.kt index 588a18a25..bb49614c5 100644 --- a/design/src/main/java/nl/rijksoverheid/ctr/design/ext/ContextExt.kt +++ b/design/src/main/java/nl/rijksoverheid/ctr/design/ext/ContextExt.kt @@ -8,6 +8,7 @@ import android.view.ContextThemeWrapper import android.view.accessibility.AccessibilityManager import androidx.annotation.AttrRes import androidx.appcompat.content.res.AppCompatResources +import androidx.core.content.ContextCompat import nl.rijksoverheid.ctr.design.R /* @@ -35,10 +36,17 @@ fun Context.isScreenReaderOn(): Boolean { return false } -fun Context.getThemeColor(@AttrRes attribute: Int): ColorStateList = TypedValue().let { +fun Context.getThemeColorStateList(@AttrRes attribute: Int): ColorStateList = TypedValue().let { theme.resolveAttribute( attribute, it, true ); AppCompatResources.getColorStateList(this, it.resourceId) } + +fun Context.getAttrColor(@AttrRes id: Int): Int { + val resolvedAttr = TypedValue() + this.theme.resolveAttribute(id, resolvedAttr, true) + val colorRes = resolvedAttr.run { if (resourceId != 0) resourceId else data } + return ContextCompat.getColor(this, colorRes) +} diff --git a/design/src/main/java/nl/rijksoverheid/ctr/design/views/HtmlTextViewWidget.kt b/design/src/main/java/nl/rijksoverheid/ctr/design/views/HtmlTextViewWidget.kt index ff6c9a815..a89923d7e 100644 --- a/design/src/main/java/nl/rijksoverheid/ctr/design/views/HtmlTextViewWidget.kt +++ b/design/src/main/java/nl/rijksoverheid/ctr/design/views/HtmlTextViewWidget.kt @@ -7,6 +7,7 @@ import android.text.SpannableStringBuilder import android.text.Spanned import android.text.style.BulletSpan import android.util.AttributeSet +import android.util.Log import android.view.View import android.widget.LinearLayout import android.widget.TextView @@ -40,6 +41,14 @@ class HtmlTextViewWidget @JvmOverloads constructor( private val HEADING_MARGIN_MULTIPLIER = 1.0f private val LIST_ITEM_MARGIN_MULTIPLIER = 0.25f + private val textColorPrimary by lazy { + context.getAttrColor(android.R.attr.textColorPrimary) + } + + private val textColorLink by lazy { + context.getAttrColor(android.R.attr.textColorLink) + } + // Reflects the unparsed HTML text shown in the subviews. Can only be set internally. var text: String? = null private set @@ -59,10 +68,12 @@ class HtmlTextViewWidget @JvmOverloads constructor( if (htmlText?.isNotEmpty() == true) { setHtmlText( htmlText = htmlText.toString(), + htmlTextColor = getColor(R.styleable.HtmlTextViewWidget_htmlTextColor, textColorPrimary), + htmlTextColorLink = getColor(R.styleable.HtmlTextViewWidget_htmlTextColorLink, textColorLink), htmlLinksEnabled = getBoolean(R.styleable.HtmlTextViewWidget_enableHtmlLinks, HTML_LINKS_ENABLED), paragraphMarginMultiplier = getFloat(R.styleable.HtmlTextViewWidget_enableHtmlLinks, PARAGRAPH_MARGIN_MULTIPLIER), headingMarginMultiplier = getFloat(R.styleable.HtmlTextViewWidget_enableHtmlLinks, HEADING_MARGIN_MULTIPLIER), - listItemMarginMultiplier = getFloat(R.styleable.HtmlTextViewWidget_enableHtmlLinks, LIST_ITEM_MARGIN_MULTIPLIER) + listItemMarginMultiplier = getFloat(R.styleable.HtmlTextViewWidget_enableHtmlLinks, LIST_ITEM_MARGIN_MULTIPLIER), ) } } finally { @@ -89,6 +100,8 @@ class HtmlTextViewWidget @JvmOverloads constructor( fun setHtmlText( htmlText: String, htmlLinksEnabled: Boolean = HTML_LINKS_ENABLED, + htmlTextColor: Int = textColorPrimary, + htmlTextColorLink: Int = textColorLink, paragraphMarginMultiplier: Float = PARAGRAPH_MARGIN_MULTIPLIER, headingMarginMultiplier: Float = HEADING_MARGIN_MULTIPLIER, listItemMarginMultiplier: Float = LIST_ITEM_MARGIN_MULTIPLIER @@ -109,6 +122,8 @@ class HtmlTextViewWidget @JvmOverloads constructor( // Step 3: Add a HtmlTextView for each part of the Spannable parts.forEachIndexed { index, part -> val textView = HtmlTextView(context) + textView.setTextColor(htmlTextColor) + textView.setLinkTextColor(htmlTextColorLink) textView.text = part // Mark as heading? diff --git a/design/src/main/java/nl/rijksoverheid/ctr/design/views/OutlinedToggleButtonWidget.kt b/design/src/main/java/nl/rijksoverheid/ctr/design/views/OutlinedToggleButtonWidget.kt index 7eb3c2b67..cdd5d56f7 100644 --- a/design/src/main/java/nl/rijksoverheid/ctr/design/views/OutlinedToggleButtonWidget.kt +++ b/design/src/main/java/nl/rijksoverheid/ctr/design/views/OutlinedToggleButtonWidget.kt @@ -15,7 +15,7 @@ import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat import com.google.android.material.button.MaterialButton import nl.rijksoverheid.ctr.design.R -import nl.rijksoverheid.ctr.design.ext.getThemeColor +import nl.rijksoverheid.ctr.design.ext.getThemeColorStateList class OutlinedToggleButtonWidget @JvmOverloads constructor( context: Context, @@ -32,7 +32,7 @@ class OutlinedToggleButtonWidget @JvmOverloads constructor( fun setToggled(isToggled: Boolean) { this.isToggled = isToggled if (isToggled) { - backgroundTintList = context.getThemeColor(R.attr.colorSurface) + backgroundTintList = context.getThemeColorStateList(R.attr.colorSurface) setStrokeColorResource(R.color.primary_blue) ViewCompat.setElevation(this, 2f) contentDescription = "$text - ${context.getString(R.string.accessibility_label_selected)}" diff --git a/design/src/main/res/values/attrs.xml b/design/src/main/res/values/attrs.xml index b460af948..4cff2af4b 100644 --- a/design/src/main/res/values/attrs.xml +++ b/design/src/main/res/values/attrs.xml @@ -12,6 +12,8 @@ + + diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt index d8620e58c..d56ef4c60 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt @@ -11,7 +11,7 @@ package nl.rijksoverheid.ctr.holder.ui.myoverview.items import android.view.View import androidx.core.content.ContextCompat import com.xwray.groupie.viewbinding.BindableItem -import nl.rijksoverheid.ctr.design.ext.getThemeColor +import nl.rijksoverheid.ctr.design.ext.getThemeColorStateList import nl.rijksoverheid.ctr.holder.R import nl.rijksoverheid.ctr.holder.databinding.ItemMyOverviewGreenCardBinding import nl.rijksoverheid.ctr.holder.persistence.database.DatabaseSyncerResult @@ -95,13 +95,12 @@ class MyOverviewGreenCardAdapterItem( viewBinding.proof2Subtitle.visibility = View.GONE viewBinding.proof3Title.visibility = View.GONE viewBinding.proof3Subtitle.visibility = View.GONE - viewBinding.proof1Subtitle.setTextColor(context.getThemeColor(android.R.attr.textColorPrimary)) - viewBinding.proof2Subtitle.setTextColor(context.getThemeColor(android.R.attr.textColorPrimary)) - viewBinding.proof3Subtitle.setTextColor(context.getThemeColor(android.R.attr.textColorPrimary)) + viewBinding.proof1Subtitle.setTextColor(context.getThemeColorStateList(android.R.attr.textColorPrimary)) + viewBinding.proof2Subtitle.setTextColor(context.getThemeColorStateList(android.R.attr.textColorPrimary)) + viewBinding.proof3Subtitle.setTextColor(context.getThemeColorStateList(android.R.attr.textColorPrimary)) viewBinding.errorText.setHtmlText("") viewBinding.errorIcon.visibility = View.GONE viewBinding.errorText.visibility = View.GONE - viewBinding.errorTextRetry.visibility = View.GONE myOverViewGreenCardAdapterUtil.setContent(greenCard, originStates, ViewBindingWrapperImpl(viewBinding)) @@ -109,32 +108,26 @@ class MyOverviewGreenCardAdapterItem( } private fun showError(viewBinding: ItemMyOverviewGreenCardBinding) { + val context = viewBinding.root.context if (credentialState is DashboardItem.GreenCardItem.CredentialState.NoCredential) { + viewBinding.errorIcon.visibility = View.VISIBLE when (databaseSyncerResult) { is DatabaseSyncerResult.Failed.NetworkError -> { - viewBinding.errorText.setHtmlText(R.string.my_overview_green_card_internet_error) + viewBinding.errorText.setHtmlText( + htmlText = context.getString(R.string.my_overview_green_card_internet_error), + htmlTextColor = ContextCompat.getColor(context, R.color.error), + htmlTextColorLink = ContextCompat.getColor(context, R.color.error)) viewBinding.errorText.enableCustomLinks(onRetryClick) viewBinding.errorIcon.visibility = View.VISIBLE viewBinding.errorText.visibility = View.VISIBLE - viewBinding.errorTextRetry.visibility = View.GONE } is DatabaseSyncerResult.Failed.ServerError -> { - when (errorState) { - DashboardErrorState.HelpdeskErrorState -> { - viewBinding.errorText.visibility = View.GONE - viewBinding.errorTextRetry.visibility = View.VISIBLE - } - DashboardErrorState.None -> { - viewBinding.errorText.visibility = View.GONE - viewBinding.errorTextRetry.visibility = View.GONE - } - DashboardErrorState.RetryErrorState -> { - viewBinding.errorText.setHtmlText(R.string.my_overview_green_card_server_error) - viewBinding.errorText.enableCustomLinks(onRetryClick) - viewBinding.errorText.visibility = View.VISIBLE - viewBinding.errorTextRetry.visibility = View.GONE - } - } + viewBinding.errorText.setHtmlText( + htmlText = context.getString(R.string.my_overview_green_card_server_error), + htmlTextColor = ContextCompat.getColor(context, R.color.error), + htmlTextColorLink = ContextCompat.getColor(context, R.color.error)) + viewBinding.errorText.enableCustomLinks(onRetryClick) + viewBinding.errorText.visibility = View.VISIBLE } else -> { } diff --git a/holder/src/main/res/layout/item_my_overview_green_card.xml b/holder/src/main/res/layout/item_my_overview_green_card.xml index 435fb603e..3aa9a4384 100644 --- a/holder/src/main/res/layout/item_my_overview_green_card.xml +++ b/holder/src/main/res/layout/item_my_overview_green_card.xml @@ -150,38 +150,19 @@ - - From e2411326026f321f91bd6fb71bdc921991563dc0 Mon Sep 17 00:00:00 2001 From: Bart Nijland Date: Wed, 29 Sep 2021 12:19:43 +0200 Subject: [PATCH 10/16] Change copy on retry click --- .../database/HolderDatabaseSyncer.kt | 32 +++++++++++++------ .../ui/myoverview/DashboardViewModel.kt | 15 ++++----- .../items/MyOverviewGreenCardAdapterItem.kt | 10 +++++- holder/src/main/res/values-en/strings.xml | 2 +- holder/src/main/res/values/strings.xml | 2 +- 5 files changed, 40 insertions(+), 21 deletions(-) diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/HolderDatabaseSyncer.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/HolderDatabaseSyncer.kt index 622f0b256..2e6babec0 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/HolderDatabaseSyncer.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/HolderDatabaseSyncer.kt @@ -9,6 +9,7 @@ import nl.rijksoverheid.ctr.holder.persistence.database.usecases.* import nl.rijksoverheid.ctr.holder.ui.create_qr.util.GreenCardUtil import nl.rijksoverheid.ctr.shared.models.ErrorResult import nl.rijksoverheid.ctr.shared.models.NetworkRequestResult +import java.time.OffsetDateTime /* * Copyright (c) 2021 De Staat der Nederlanden, Ministerie van Volksgezondheid, Welzijn en Sport. @@ -24,7 +25,10 @@ interface HolderDatabaseSyncer { * @param expectedOriginType If not null checks if the remote credentials contain this origin. Will return [DatabaseSyncerResult.MissingOrigin] if it's not present. * @param syncWithRemote If true and the data call to resync succeeds, clear all green cards in the database and re-add them */ - suspend fun sync(expectedOriginType: OriginType? = null, syncWithRemote: Boolean = true): DatabaseSyncerResult + suspend fun sync( + expectedOriginType: OriginType? = null, + syncWithRemote: Boolean = true, + previousSyncResult: DatabaseSyncerResult? = null): DatabaseSyncerResult } class HolderDatabaseSyncerImpl( @@ -39,7 +43,8 @@ class HolderDatabaseSyncerImpl( override suspend fun sync( expectedOriginType: OriginType?, - syncWithRemote: Boolean + syncWithRemote: Boolean, + previousSyncResult: DatabaseSyncerResult? ): DatabaseSyncerResult { return withContext(Dispatchers.IO) { mutex.withLock { @@ -93,9 +98,15 @@ class HolderDatabaseSyncerImpl( ) } is NetworkRequestResult.Failed.CoronaCheckHttpError -> { - DatabaseSyncerResult.Failed.ServerError( - errorResult = remoteGreenCardsResult.errorResult - ) + if (previousSyncResult == null) { + DatabaseSyncerResult.Failed.ServerError.FirstTime( + errorResult = remoteGreenCardsResult.errorResult + ) + } else { + DatabaseSyncerResult.Failed.ServerError.MultipleTimes( + errorResult = remoteGreenCardsResult.errorResult + ) + } } else -> { DatabaseSyncerResult.Failed.Error( @@ -118,9 +129,12 @@ sealed class DatabaseSyncerResult { object Success : DatabaseSyncerResult() object MissingOrigin : DatabaseSyncerResult() - sealed class Failed(open val errorResult: ErrorResult): DatabaseSyncerResult() { - data class NetworkError(override val errorResult: ErrorResult, val hasGreenCardsWithoutCredentials: Boolean): Failed(errorResult) - data class ServerError(override val errorResult: ErrorResult): Failed(errorResult) - data class Error(override val errorResult: ErrorResult): Failed(errorResult) + sealed class Failed(open val errorResult: ErrorResult, open val failedAt: OffsetDateTime): DatabaseSyncerResult() { + data class NetworkError(override val errorResult: ErrorResult, val hasGreenCardsWithoutCredentials: Boolean): Failed(errorResult, OffsetDateTime.now()) + sealed class ServerError(override val errorResult: ErrorResult): Failed(errorResult, OffsetDateTime.now()) { + data class FirstTime(override val errorResult: ErrorResult) : ServerError(errorResult) + data class MultipleTimes(override val errorResult: ErrorResult) : ServerError(errorResult) + } + data class Error(override val errorResult: ErrorResult): Failed(errorResult, OffsetDateTime.now()) } } diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt index b412b1a8d..480be1072 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt @@ -44,13 +44,13 @@ class DashboardViewModelImpl( private val mutex = Mutex() - private var lastRetryUpdate: OffsetDateTime? = null - override fun refresh(dashboardSync: DashboardSync) { viewModelScope.launch { mutex.withLock { // Check if we need to refresh our data - val hasDoneRefreshCall = databaseSyncerResultLiveData.value?.peekContent() != null + val previousSyncResult = databaseSyncerResultLiveData.value?.peekContent() + val hasDoneRefreshCall = previousSyncResult != null + val shouldLoadNewCredentials = when (dashboardSync) { is DashboardSync.ForceSync -> { true @@ -73,7 +73,8 @@ class DashboardViewModelImpl( ) val databaseSyncerResult = holderDatabaseSyncer.sync( - syncWithRemote = shouldLoadNewCredentials + syncWithRemote = shouldLoadNewCredentials, + previousSyncResult = previousSyncResult ) (databaseSyncerResultLiveData as MutableLiveData).postValue( @@ -82,6 +83,7 @@ class DashboardViewModelImpl( // If we loaded new credentials, we want to update our items again if (shouldLoadNewCredentials) { + // Set the last time we loaded new credentials refreshDashboardTabItems( allGreenCards = allGreenCards, databaseSyncerResult = databaseSyncerResult, @@ -92,11 +94,6 @@ class DashboardViewModelImpl( } } - private fun shouldAllowRetry(): Boolean { - val lastUpdate = lastRetryUpdate ?: return true - return OffsetDateTime.now().isAfter(lastUpdate.plusMinutes(retryIntervalMinutes)) - } - override fun removeGreenCard(greenCard: GreenCard) { viewModelScope.launch { holderDatabase.greenCardDao().delete(greenCard.greenCardEntity) diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt index d56ef4c60..47540de3e 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt @@ -121,7 +121,7 @@ class MyOverviewGreenCardAdapterItem( viewBinding.errorIcon.visibility = View.VISIBLE viewBinding.errorText.visibility = View.VISIBLE } - is DatabaseSyncerResult.Failed.ServerError -> { + is DatabaseSyncerResult.Failed.ServerError.FirstTime -> { viewBinding.errorText.setHtmlText( htmlText = context.getString(R.string.my_overview_green_card_server_error), htmlTextColor = ContextCompat.getColor(context, R.color.error), @@ -129,7 +129,15 @@ class MyOverviewGreenCardAdapterItem( viewBinding.errorText.enableCustomLinks(onRetryClick) viewBinding.errorText.visibility = View.VISIBLE } + is DatabaseSyncerResult.Failed.ServerError.MultipleTimes -> { + viewBinding.errorText.setHtmlText( + htmlText = context.getString(R.string.my_overview_green_card_server_error_after_retry), + htmlTextColor = ContextCompat.getColor(context, R.color.error), + htmlTextColorLink = ContextCompat.getColor(context, R.color.error)) + viewBinding.errorText.visibility = View.VISIBLE + } else -> { + } } } diff --git a/holder/src/main/res/values-en/strings.xml b/holder/src/main/res/values-en/strings.xml index 58b07b5f8..f75d57ceb 100644 --- a/holder/src/main/res/values-en/strings.xml +++ b/holder/src/main/res/values-en/strings.xml @@ -370,7 +370,7 @@
You’ve only got an international QR code.]]>

The reason is that the type of test is not accepted. An international recovery certificate can only be made with a PCR-test.]]>
Try again]]> - No success? Please call the help desk at 0800-1421 (free)]]> +
No success? Please call the help desk at 0800-1421 (free)]]>
Try again]]> Vaccination certificate corrected You’ve wrongly received a Dutch vaccination certificate due to a technical error. This was corrected. diff --git a/holder/src/main/res/values/strings.xml b/holder/src/main/res/values/strings.xml index eb25f238e..6a1ad37ca 100644 --- a/holder/src/main/res/values/strings.xml +++ b/holder/src/main/res/values/strings.xml @@ -370,7 +370,7 @@
Dit komt doordat het type test niet voldoet in Nederland.]]>

Dit komt doordat het type test niet voldoet. Alleen van een PCR-test kan een internationaal bewijs gemaakt worden.]]>
Probeer opnieuw]]> - Helpt dat niet? Bel dan de helpdesk op 0800-1421 (gratis)]]> +
Helpt dat niet? Bel dan de helpdesk op 0800-1421 (gratis)]]>
Probeer opnieuw]]> Vaccinatiebewijs gecorrigeerd Je hebt een Nederlands vaccinatiebewijs gekregen door een fout. Dit is nu gecorrigeerd. From c9c5db353d1c2532355dcf687589469652408284 Mon Sep 17 00:00:00 2001 From: Bart Nijland Date: Wed, 29 Sep 2021 14:03:14 +0200 Subject: [PATCH 11/16] Cleanup --- .../database/HolderDatabaseSyncer.kt | 4 +-- .../ui/myoverview/DashboardViewModel.kt | 29 +++++++++++++------ .../ui/myoverview/MyOverviewTabsFragment.kt | 2 +- .../myoverview/models/DashboardSyncState.kt | 2 +- .../java/nl/rijksoverheid/ctr/holder/Fakes.kt | 5 ++-- 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/HolderDatabaseSyncer.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/HolderDatabaseSyncer.kt index 2e6babec0..abbaba838 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/HolderDatabaseSyncer.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/persistence/database/HolderDatabaseSyncer.kt @@ -24,6 +24,7 @@ interface HolderDatabaseSyncer { * Synchronized the database. Does cleanup in the database based on expiration dates and can resync with remote * @param expectedOriginType If not null checks if the remote credentials contain this origin. Will return [DatabaseSyncerResult.MissingOrigin] if it's not present. * @param syncWithRemote If true and the data call to resync succeeds, clear all green cards in the database and re-add them + * @param previousSyncResult The previous result outputted by this [sync] if known */ suspend fun sync( expectedOriginType: OriginType? = null, @@ -117,7 +118,7 @@ class HolderDatabaseSyncerImpl( } } } else { - DatabaseSyncerResult.Success + previousSyncResult ?: DatabaseSyncerResult.Success } } } @@ -125,7 +126,6 @@ class HolderDatabaseSyncerImpl( } sealed class DatabaseSyncerResult { - object Loading : DatabaseSyncerResult() object Success : DatabaseSyncerResult() object MissingOrigin : DatabaseSyncerResult() diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt index 480be1072..f8749ea3c 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/DashboardViewModel.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock +import nl.rijksoverheid.ctr.holder.BuildConfig import nl.rijksoverheid.ctr.holder.R import nl.rijksoverheid.ctr.holder.persistence.database.DatabaseSyncerResult import nl.rijksoverheid.ctr.holder.persistence.database.HolderDatabase @@ -20,6 +21,7 @@ import nl.rijksoverheid.ctr.holder.ui.myoverview.models.DashboardSync import nl.rijksoverheid.ctr.holder.ui.myoverview.models.DashboardTabItem import nl.rijksoverheid.ctr.shared.livedata.Event import java.time.OffsetDateTime +import java.util.concurrent.TimeUnit abstract class DashboardViewModel : ViewModel() { open val dashboardTabItemsLiveData: LiveData> = MutableLiveData() @@ -27,11 +29,11 @@ abstract class DashboardViewModel : ViewModel() { var dashboardErrorState: DashboardErrorState = DashboardErrorState.None - abstract fun refresh(dashboardSync: DashboardSync = DashboardSync.CheckCredentialsExpired) + abstract fun refresh(dashboardSync: DashboardSync = DashboardSync.CheckSync) abstract fun removeGreenCard(greenCard: GreenCard) companion object { - internal const val retryIntervalMinutes = 10L + val RETRY_FAILED_REQUEST_AFTER_SECONDS = if (BuildConfig.FLAVOR == "acc") TimeUnit.SECONDS.toSeconds(10) else TimeUnit.MINUTES.toSeconds(10) } } @@ -44,22 +46,34 @@ class DashboardViewModelImpl( private val mutex = Mutex() + /** + * Refreshing of database happens every 60 seconds + */ override fun refresh(dashboardSync: DashboardSync) { viewModelScope.launch { mutex.withLock { - // Check if we need to refresh our data val previousSyncResult = databaseSyncerResultLiveData.value?.peekContent() val hasDoneRefreshCall = previousSyncResult != null + // Check if we need to load new credentials val shouldLoadNewCredentials = when (dashboardSync) { is DashboardSync.ForceSync -> { + // Load new credentials if we force it. For example on a retry button click true } is DashboardSync.DisableSync -> { + // Never load new credentials when we don't want to. For example if we are checking to show the clock skew banner false } - is DashboardSync.CheckCredentialsExpired -> { - (greenCardRefreshUtil.shouldRefresh() && !hasDoneRefreshCall) + is DashboardSync.CheckSync -> { + // Load new credentials if no previous refresh has been executed and we should refresh because a credentials for a green card expired + val shouldRefreshCredentials = (greenCardRefreshUtil.shouldRefresh() && !hasDoneRefreshCall) + + // Load new credentials if we the previous request failed more than once and more than x minutes ago + val shouldRetryFailedRequest = previousSyncResult is DatabaseSyncerResult.Failed.ServerError.MultipleTimes && OffsetDateTime.now().isAfter(previousSyncResult.failedAt.plusSeconds(RETRY_FAILED_REQUEST_AFTER_SECONDS)) + + // Do the actual checks + shouldRefreshCredentials || shouldRetryFailedRequest } } @@ -77,13 +91,10 @@ class DashboardViewModelImpl( previousSyncResult = previousSyncResult ) - (databaseSyncerResultLiveData as MutableLiveData).postValue( - Event(databaseSyncerResult) - ) + (databaseSyncerResultLiveData as MutableLiveData).value = Event(databaseSyncerResult) // If we loaded new credentials, we want to update our items again if (shouldLoadNewCredentials) { - // Set the last time we loaded new credentials refreshDashboardTabItems( allGreenCards = allGreenCards, databaseSyncerResult = databaseSyncerResult, diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewTabsFragment.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewTabsFragment.kt index 018831361..e259c9ea9 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewTabsFragment.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/MyOverviewTabsFragment.kt @@ -151,7 +151,7 @@ class MyOverviewTabsFragment : Fragment(R.layout.fragment_tabs_my_overview) { ) } - private fun refresh(dashboardSync: DashboardSync = DashboardSync.CheckCredentialsExpired) { + private fun refresh(dashboardSync: DashboardSync = DashboardSync.CheckSync) { dashboardViewModel.refresh(dashboardSync) refreshHandler.postDelayed( refreshRunnable, diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/models/DashboardSyncState.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/models/DashboardSyncState.kt index 85318796e..9cbd3169c 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/models/DashboardSyncState.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/models/DashboardSyncState.kt @@ -3,5 +3,5 @@ package nl.rijksoverheid.ctr.holder.ui.myoverview.models sealed class DashboardSync { object ForceSync: DashboardSync() object DisableSync: DashboardSync() - object CheckCredentialsExpired: DashboardSync() + object CheckSync: DashboardSync() } \ No newline at end of file diff --git a/holder/src/test/java/nl/rijksoverheid/ctr/holder/Fakes.kt b/holder/src/test/java/nl/rijksoverheid/ctr/holder/Fakes.kt index 07d8bb87c..ec9e171d5 100644 --- a/holder/src/test/java/nl/rijksoverheid/ctr/holder/Fakes.kt +++ b/holder/src/test/java/nl/rijksoverheid/ctr/holder/Fakes.kt @@ -19,6 +19,7 @@ import nl.rijksoverheid.ctr.holder.ui.create_qr.repositories.TestProviderReposit import nl.rijksoverheid.ctr.holder.ui.create_qr.usecases.* import nl.rijksoverheid.ctr.holder.ui.create_qr.util.* import nl.rijksoverheid.ctr.holder.ui.myoverview.DashboardViewModel +import nl.rijksoverheid.ctr.holder.ui.myoverview.models.DashboardSync import nl.rijksoverheid.ctr.holder.ui.myoverview.usecases.TestResultAttributesUseCase import nl.rijksoverheid.ctr.holder.ui.myoverview.utils.TokenValidatorUtil import nl.rijksoverheid.ctr.introduction.IntroductionData @@ -50,12 +51,12 @@ fun fakeAppConfigViewModel(appStatus: AppStatus = AppStatus.NoActionRequired) = fun fakeDashboardViewModel() = object : DashboardViewModel() { - override fun refresh(forceSync: Boolean) { + override fun refresh(dashboardSync: DashboardSync) { } override fun removeGreenCard(greenCard: GreenCard) { - + } } From 3ab8970b00d49c5100edcb26d2bec692b210d8b6 Mon Sep 17 00:00:00 2001 From: Bart Nijland Date: Wed, 29 Sep 2021 14:29:24 +0200 Subject: [PATCH 12/16] Fix test --- .../ctr/holder/persistence/RefreshCredentialsJobTest.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/holder/src/test/java/nl/rijksoverheid/ctr/holder/persistence/RefreshCredentialsJobTest.kt b/holder/src/test/java/nl/rijksoverheid/ctr/holder/persistence/RefreshCredentialsJobTest.kt index b2cc6a9b7..014a91236 100644 --- a/holder/src/test/java/nl/rijksoverheid/ctr/holder/persistence/RefreshCredentialsJobTest.kt +++ b/holder/src/test/java/nl/rijksoverheid/ctr/holder/persistence/RefreshCredentialsJobTest.kt @@ -43,7 +43,7 @@ class RefreshCredentialsJobTest: AutoCloseKoinTest() { fun `given a unsuccessful database sync, when worker does work, then it returns retry`() { val context = ApplicationProvider.getApplicationContext() val worker = TestListenableWorkerBuilder(context).setWorkerFactory( - testWorkerFactory(databaseSyncerResult = DatabaseSyncerResult.Failed.ServerError(AppErrorResult(HolderStep.GetCredentialsNetworkRequest, IllegalStateException()))) + testWorkerFactory(databaseSyncerResult = DatabaseSyncerResult.Failed.ServerError.FirstTime(AppErrorResult(HolderStep.GetCredentialsNetworkRequest, IllegalStateException()))) ).build() val result = worker.startWork().get() @@ -65,8 +65,11 @@ class RefreshCredentialsJobTest: AutoCloseKoinTest() { holderDatabaseSyncer = object: HolderDatabaseSyncer { override suspend fun sync( expectedOriginType: OriginType?, - syncWithRemote: Boolean - ): DatabaseSyncerResult = databaseSyncerResult + syncWithRemote: Boolean, + previousSyncResult: DatabaseSyncerResult? + ): DatabaseSyncerResult { + return databaseSyncerResult + } } ) } \ No newline at end of file From 3a89c83cb81f467ad67de65c147c29d587996c68 Mon Sep 17 00:00:00 2001 From: Bart Nijland Date: Wed, 29 Sep 2021 15:15:47 +0200 Subject: [PATCH 13/16] Only show error icon on error --- .../ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt index 47540de3e..ffd7ee0a3 100644 --- a/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt +++ b/holder/src/main/java/nl/rijksoverheid/ctr/holder/ui/myoverview/items/MyOverviewGreenCardAdapterItem.kt @@ -110,7 +110,6 @@ class MyOverviewGreenCardAdapterItem( private fun showError(viewBinding: ItemMyOverviewGreenCardBinding) { val context = viewBinding.root.context if (credentialState is DashboardItem.GreenCardItem.CredentialState.NoCredential) { - viewBinding.errorIcon.visibility = View.VISIBLE when (databaseSyncerResult) { is DatabaseSyncerResult.Failed.NetworkError -> { viewBinding.errorText.setHtmlText( @@ -127,6 +126,7 @@ class MyOverviewGreenCardAdapterItem( htmlTextColor = ContextCompat.getColor(context, R.color.error), htmlTextColorLink = ContextCompat.getColor(context, R.color.error)) viewBinding.errorText.enableCustomLinks(onRetryClick) + viewBinding.errorIcon.visibility = View.VISIBLE viewBinding.errorText.visibility = View.VISIBLE } is DatabaseSyncerResult.Failed.ServerError.MultipleTimes -> { @@ -135,6 +135,7 @@ class MyOverviewGreenCardAdapterItem( htmlTextColor = ContextCompat.getColor(context, R.color.error), htmlTextColorLink = ContextCompat.getColor(context, R.color.error)) viewBinding.errorText.visibility = View.VISIBLE + viewBinding.errorIcon.visibility = View.VISIBLE } else -> { From 9f7bed5d843680cd4349920ddc687cfcaad86206 Mon Sep 17 00:00:00 2001 From: Aiden Shi Date: Wed, 29 Sep 2021 15:29:42 +0200 Subject: [PATCH 14/16] define acc cdn urls --- verifier/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/verifier/build.gradle b/verifier/build.gradle index bc72db6e1..f499f9108 100644 --- a/verifier/build.gradle +++ b/verifier/build.gradle @@ -80,6 +80,7 @@ android { applicationIdSuffix ".acc" manifestPlaceholders = [appLabel: "@string/app_name_acc"] buildConfigField "String", "BASE_API_URL", "\"https://verifier-api.acc.coronacheck.nl/v5/\"" + buildConfigField "String", "CDN_API_URL", "\"https://verifier-api-cdn.acc.coronacheck.nl/v5/\"" buildConfigField "String[]", "CERTIFICATE_PINS", "{" + "\"sha256/lR7gRvqDMW5nhsCMRPE7TKLq0tJkTWMxQ5HAzHCIfQ0=\"" + From 73674512fc766ff8c205b80c4a6fbdf39beb977f Mon Sep 17 00:00:00 2001 From: Aiden Shi Date: Wed, 29 Sep 2021 15:30:31 +0200 Subject: [PATCH 15/16] set holder cdn url --- holder/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/holder/build.gradle b/holder/build.gradle index 322841934..8f59393bc 100644 --- a/holder/build.gradle +++ b/holder/build.gradle @@ -90,6 +90,7 @@ android { } acc { buildConfigField "String", "BASE_API_URL", "\"https://holder-api.acc.coronacheck.nl/v5/\"" + buildConfigField "String", "CDN_API_URL", "\"https://holder-api-cdn.acc.coronacheck.nl/v5/\"" buildConfigField "String", "DIGI_D_BASE_URL", "\"https://tvs.acc.coronacheck.nl\"" buildConfigField "String", "DIGI_D_CLIENT_ID", "\"cc_app\"" buildConfigField "String", "DIGI_D_REDIRECT_URI", "\"https://web.acc.coronacheck.nl/app/auth2\"" From b3b2aa0ec4e258dd63221bc5ca13d16e6a2814dc Mon Sep 17 00:00:00 2001 From: Bart Nijland Date: Wed, 29 Sep 2021 15:47:12 +0200 Subject: [PATCH 16/16] Fix copy --- holder/src/main/res/values-en/strings.xml | 2 +- holder/src/main/res/values/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/holder/src/main/res/values-en/strings.xml b/holder/src/main/res/values-en/strings.xml index f75d57ceb..46d2450f6 100644 --- a/holder/src/main/res/values-en/strings.xml +++ b/holder/src/main/res/values-en/strings.xml @@ -370,7 +370,7 @@
You’ve only got an international QR code.]]>

The reason is that the type of test is not accepted. An international recovery certificate can only be made with a PCR-test.]]>
Try again]]> -
No success? Please call the help desk at 0800-1421 (free)]]>
+
Still not working? Please call the help desk at 0800-1421 (free) and provide the following error code: A 075]]>
Try again]]> Vaccination certificate corrected You’ve wrongly received a Dutch vaccination certificate due to a technical error. This was corrected. diff --git a/holder/src/main/res/values/strings.xml b/holder/src/main/res/values/strings.xml index 6a1ad37ca..1e658ede2 100644 --- a/holder/src/main/res/values/strings.xml +++ b/holder/src/main/res/values/strings.xml @@ -370,7 +370,7 @@
Dit komt doordat het type test niet voldoet in Nederland.]]>

Dit komt doordat het type test niet voldoet. Alleen van een PCR-test kan een internationaal bewijs gemaakt worden.]]>
Probeer opnieuw]]> -
Helpt dat niet? Bel dan de helpdesk op 0800-1421 (gratis)]]>
+
Helpt dat niet? Bel dan de helpdesk op 0800-1421 (gratis) en geef de foutcode (A 075) door.]]>
Probeer opnieuw]]> Vaccinatiebewijs gecorrigeerd Je hebt een Nederlands vaccinatiebewijs gekregen door een fout. Dit is nu gecorrigeerd.