Skip to content

Commit 7726e97

Browse files
committed
feat: implement configurable PID activation and update document issuance rules
This commit introduces a `forcePidActivation` flag in `ConfigLogic` to control whether the wallet requires a PID (Personal Identification Data) to be active. It refactors the startup and PIN flows to handle both activation and non-activation scenarios. Key changes include: - **Config & Logic**: Added `forcePidActivation` to `ConfigLogic` (defaulting to `true`). Moved `documentIssuanceConfig` from `WalletCoreConfig` to flavor-specific implementations (`dev` and `demo`), increasing credential counts for PID documents in `dev`. - **Startup & PIN Flow**: Updated `SplashInteractor` and `PinViewModel` to differentiate between `CREATE_WITH_ACTIVATION` and `CREATE_WITHOUT_ACTIVATION` flows. This affects navigation and the success messages displayed after PIN creation. - **Dashboard & Documents**: Refactored `DocumentsInteractor` and `DocumentDetailsInteractor` to respect the `forcePidActivation` flag when determining if all documents should be deleted or if the user should be redirected to the issuance screen. - **Resources**: Added new string resources for the "no activation" success state. - **Dependencies**: Updated `coil` to `3.4.0` and `googlePhoneNumber` to `9.0.24` in `libs.versions.toml`.
1 parent da62879 commit 7726e97

File tree

16 files changed

+147
-50
lines changed

16 files changed

+147
-50
lines changed

business-logic/src/main/java/eu/europa/ec/businesslogic/config/ConfigLogic.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ interface ConfigLogic {
5353
* changelog is maintained for development builds.
5454
*/
5555
val changelogUrl: String?
56+
57+
/**
58+
* Set if the waller requires PID Activation.
59+
*/
60+
val forcePidActivation: Boolean get() = true
5661
}
5762

5863
enum class AppFlavor {

common-feature/src/main/java/eu/europa/ec/commonfeature/model/PinFlow.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package eu.europa.ec.commonfeature.model
1818

1919
enum class PinFlow {
20-
CREATE,
20+
CREATE_WITHOUT_ACTIVATION,
21+
CREATE_WITH_ACTIVATION,
2122
UPDATE
2223
}

common-feature/src/main/java/eu/europa/ec/commonfeature/ui/pin/PinScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ private fun PinScreenEmptyPreview() {
299299
PreviewTheme {
300300
Content(
301301
state = State(
302-
pinFlow = PinFlow.CREATE,
302+
pinFlow = PinFlow.CREATE_WITH_ACTIVATION,
303303
pinState = PinValidationState.ENTER
304304
),
305305
effectFlow = Channel<Effect>().receiveAsFlow(),

common-feature/src/main/java/eu/europa/ec/commonfeature/ui/pin/PinViewModel.kt

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,15 @@ data class State(
7474
val action: ScreenNavigateAction
7575
get() {
7676
return when (pinFlow) {
77-
PinFlow.CREATE -> ScreenNavigateAction.NONE
77+
PinFlow.CREATE_WITH_ACTIVATION, PinFlow.CREATE_WITHOUT_ACTIVATION -> ScreenNavigateAction.NONE
7878
PinFlow.UPDATE -> ScreenNavigateAction.CANCELABLE
7979
}
8080
}
8181

8282
val onBackEvent: Event
8383
get() {
8484
return when (pinFlow) {
85-
PinFlow.CREATE -> Event.Finish
85+
PinFlow.CREATE_WITH_ACTIVATION, PinFlow.CREATE_WITHOUT_ACTIVATION -> Event.Finish
8686
PinFlow.UPDATE -> Event.CancelPressed
8787
}
8888
}
@@ -131,7 +131,7 @@ class PinViewModel(
131131
val buttonText: String
132132

133133
when (pinFlow) {
134-
PinFlow.CREATE -> {
134+
PinFlow.CREATE_WITH_ACTIVATION, PinFlow.CREATE_WITHOUT_ACTIVATION -> {
135135
title = resourceProvider.getString(R.string.quick_pin_create_title)
136136
subtitle = resourceProvider.getString(R.string.quick_pin_create_enter_subtitle)
137137
pinState = PinValidationState.ENTER
@@ -330,7 +330,7 @@ class PinViewModel(
330330
}
331331
}
332332

333-
PinFlow.CREATE -> {
333+
PinFlow.CREATE_WITH_ACTIVATION, PinFlow.CREATE_WITHOUT_ACTIVATION -> {
334334
when (pinState) {
335335
PinValidationState.ENTER -> resourceProvider.getString(R.string.quick_pin_create_enter_subtitle)
336336
PinValidationState.REENTER -> resourceProvider.getString(R.string.quick_pin_create_reenter_subtitle)
@@ -369,6 +369,13 @@ class PinViewModel(
369369
navigationType = NavigationType.PopTo(DashboardScreens.Dashboard),
370370
)
371371

372+
val navigationAfterCreateNoActivation = ConfigNavigation(
373+
navigationType = NavigationType.PushScreen(
374+
screen = DashboardScreens.Dashboard,
375+
popUpToScreen = CommonScreens.QuickPin
376+
),
377+
)
378+
372379
return generateComposableNavigationLink(
373380
screen = CommonScreens.Success,
374381
arguments = generateComposableArguments(
@@ -377,16 +384,23 @@ class PinViewModel(
377384
SuccessUIConfig(
378385
textElementsConfig = SuccessUIConfig.TextElementsConfig(
379386
text = when (pinFlow) {
380-
PinFlow.CREATE -> resourceProvider.getString(R.string.quick_pin_create_success_text)
387+
PinFlow.CREATE_WITH_ACTIVATION, PinFlow.CREATE_WITHOUT_ACTIVATION -> resourceProvider.getString(
388+
R.string.quick_pin_create_success_text
389+
)
390+
381391
PinFlow.UPDATE -> resourceProvider.getString(R.string.quick_pin_change_success_text)
382392
},
383393
description = when (pinFlow) {
384-
PinFlow.CREATE -> resourceProvider.getString(R.string.quick_pin_create_success_description)
394+
PinFlow.CREATE_WITH_ACTIVATION -> resourceProvider.getString(R.string.quick_pin_create_success_description)
395+
PinFlow.CREATE_WITHOUT_ACTIVATION -> resourceProvider.getString(
396+
R.string.quick_pin_create_success_no_activation_description
397+
)
398+
385399
PinFlow.UPDATE -> resourceProvider.getString(R.string.quick_pin_change_success_description)
386400
}
387401
),
388402
imageConfig = when (pinFlow) {
389-
PinFlow.CREATE -> SuccessUIConfig.ImageConfig(
403+
PinFlow.CREATE_WITH_ACTIVATION, PinFlow.CREATE_WITHOUT_ACTIVATION -> SuccessUIConfig.ImageConfig(
390404
type = SuccessUIConfig.ImageConfig.Type.Drawable(
391405
icon = AppIcons.WalletSecured
392406
),
@@ -398,18 +412,27 @@ class PinViewModel(
398412
buttonConfig = listOf(
399413
SuccessUIConfig.ButtonConfig(
400414
text = when (pinFlow) {
401-
PinFlow.CREATE -> resourceProvider.getString(R.string.quick_pin_create_success_btn)
415+
PinFlow.CREATE_WITH_ACTIVATION -> resourceProvider.getString(
416+
R.string.quick_pin_create_success_btn
417+
)
418+
419+
PinFlow.CREATE_WITHOUT_ACTIVATION -> resourceProvider.getString(
420+
R.string.quick_pin_create_success_no_activation_btn
421+
)
422+
402423
PinFlow.UPDATE -> resourceProvider.getString(R.string.quick_pin_change_success_btn)
403424
},
404425
style = SuccessUIConfig.ButtonConfig.Style.PRIMARY,
405426
navigation = when (pinFlow) {
406-
PinFlow.CREATE -> navigationAfterCreate
427+
PinFlow.CREATE_WITH_ACTIVATION -> navigationAfterCreate
428+
PinFlow.CREATE_WITHOUT_ACTIVATION -> navigationAfterCreateNoActivation
407429
PinFlow.UPDATE -> navigationAfterUpdate
408430
}
409431
)
410432
),
411433
onBackScreenToNavigate = when (pinFlow) {
412-
PinFlow.CREATE -> navigationAfterCreate
434+
PinFlow.CREATE_WITH_ACTIVATION -> navigationAfterCreate
435+
PinFlow.CREATE_WITHOUT_ACTIVATION -> navigationAfterCreateNoActivation
413436
PinFlow.UPDATE -> navigationAfterUpdate
414437
},
415438
),

core-logic/src/demo/java/eu/europa/ec/corelogic/config/WalletCoreConfigImpl.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ package eu.europa.ec.corelogic.config
1818

1919
import android.content.Context
2020
import eu.europa.ec.corelogic.BuildConfig
21+
import eu.europa.ec.corelogic.model.DocumentIdentifier
2122
import eu.europa.ec.eudi.wallet.EudiWalletConfig
23+
import eu.europa.ec.eudi.wallet.document.CreateDocumentSettings.CredentialPolicy
2224
import eu.europa.ec.eudi.wallet.issue.openid4vci.OpenId4VciManager
2325
import eu.europa.ec.eudi.wallet.transfer.openId4vp.ClientIdScheme
2426
import eu.europa.ec.eudi.wallet.transfer.openId4vp.Format
@@ -95,6 +97,24 @@ internal class WalletCoreConfigImpl(
9597
.build()
9698
)
9799

100+
override val documentIssuanceConfig: DocumentIssuanceConfig
101+
get() = DocumentIssuanceConfig(
102+
defaultRule = DocumentIssuanceRule(
103+
policy = CredentialPolicy.RotateUse,
104+
numberOfCredentials = 1
105+
),
106+
documentSpecificRules = mapOf(
107+
DocumentIdentifier.MdocPid to DocumentIssuanceRule(
108+
policy = CredentialPolicy.OneTimeUse,
109+
numberOfCredentials = 10
110+
),
111+
DocumentIdentifier.SdJwtPid to DocumentIssuanceRule(
112+
policy = CredentialPolicy.OneTimeUse,
113+
numberOfCredentials = 10
114+
),
115+
)
116+
)
117+
98118
override val walletProviderHost: String
99119
get() = "https://wallet-provider.eudiw.dev"
100120
}

core-logic/src/dev/java/eu/europa/ec/corelogic/config/WalletCoreConfigImpl.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ package eu.europa.ec.corelogic.config
1818

1919
import android.content.Context
2020
import eu.europa.ec.corelogic.BuildConfig
21+
import eu.europa.ec.corelogic.model.DocumentIdentifier
2122
import eu.europa.ec.eudi.wallet.EudiWalletConfig
23+
import eu.europa.ec.eudi.wallet.document.CreateDocumentSettings.CredentialPolicy
2224
import eu.europa.ec.eudi.wallet.issue.openid4vci.OpenId4VciManager
2325
import eu.europa.ec.eudi.wallet.transfer.openId4vp.ClientIdScheme
2426
import eu.europa.ec.eudi.wallet.transfer.openId4vp.Format
@@ -95,6 +97,24 @@ internal class WalletCoreConfigImpl(
9597
.build()
9698
)
9799

100+
override val documentIssuanceConfig: DocumentIssuanceConfig
101+
get() = DocumentIssuanceConfig(
102+
defaultRule = DocumentIssuanceRule(
103+
policy = CredentialPolicy.RotateUse,
104+
numberOfCredentials = 1
105+
),
106+
documentSpecificRules = mapOf(
107+
DocumentIdentifier.MdocPid to DocumentIssuanceRule(
108+
policy = CredentialPolicy.OneTimeUse,
109+
numberOfCredentials = 60
110+
),
111+
DocumentIdentifier.SdJwtPid to DocumentIssuanceRule(
112+
policy = CredentialPolicy.OneTimeUse,
113+
numberOfCredentials = 60
114+
),
115+
)
116+
)
117+
98118
override val walletProviderHost: String
99119
get() = "https://dev.wallet-provider.eudiw.dev"
100120
}

core-logic/src/main/java/eu/europa/ec/corelogic/config/WalletCoreConfig.kt

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -181,22 +181,6 @@ interface WalletCoreConfig {
181181
* Any document type not listed in `documentSpecificRules` will use the `defaultRule`.
182182
*/
183183
val documentIssuanceConfig: DocumentIssuanceConfig
184-
get() = DocumentIssuanceConfig(
185-
defaultRule = DocumentIssuanceRule(
186-
policy = CredentialPolicy.RotateUse,
187-
numberOfCredentials = 1
188-
),
189-
documentSpecificRules = mapOf(
190-
DocumentIdentifier.MdocPid to DocumentIssuanceRule(
191-
policy = CredentialPolicy.OneTimeUse,
192-
numberOfCredentials = 10
193-
),
194-
DocumentIdentifier.SdJwtPid to DocumentIssuanceRule(
195-
policy = CredentialPolicy.OneTimeUse,
196-
numberOfCredentials = 10
197-
),
198-
)
199-
)
200184

201185
/**
202186
* Host for the Wallet Provider.

dashboard-feature/src/main/java/eu/europa/ec/dashboardfeature/di/FeatureDashboardModule.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,13 @@ fun provideDocumentsInteractor(
8181
resourceProvider: ResourceProvider,
8282
documentsController: WalletCoreDocumentsController,
8383
filterValidator: FilterValidator,
84+
configLogic: ConfigLogic
8485
): DocumentsInteractor =
8586
DocumentsInteractorImpl(
8687
resourceProvider,
8788
documentsController,
8889
filterValidator,
90+
configLogic
8991
)
9092

9193
@Factory
@@ -111,11 +113,13 @@ fun provideDocumentDetailsInteractor(
111113
walletCoreDocumentsController: WalletCoreDocumentsController,
112114
resourceProvider: ResourceProvider,
113115
uuidProvider: UuidProvider,
116+
configLogic: ConfigLogic
114117
): DocumentDetailsInteractor =
115118
DocumentDetailsInteractorImpl(
116119
walletCoreDocumentsController,
117120
resourceProvider,
118121
uuidProvider,
122+
configLogic
119123
)
120124

121125
@Factory

dashboard-feature/src/main/java/eu/europa/ec/dashboardfeature/interactor/DocumentDetailsInteractor.kt

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package eu.europa.ec.dashboardfeature.interactor
1818

19+
import eu.europa.ec.businesslogic.config.ConfigLogic
1920
import eu.europa.ec.businesslogic.extension.safeAsync
2021
import eu.europa.ec.businesslogic.provider.UuidProvider
2122
import eu.europa.ec.corelogic.controller.DeleteAllDocumentsPartialState
@@ -94,6 +95,7 @@ class DocumentDetailsInteractorImpl(
9495
private val walletCoreDocumentsController: WalletCoreDocumentsController,
9596
private val resourceProvider: ResourceProvider,
9697
private val uuidProvider: UuidProvider,
98+
private val configLogic: ConfigLogic
9799
) : DocumentDetailsInteractor {
98100

99101
private val genericErrorMsg
@@ -161,24 +163,25 @@ class DocumentDetailsInteractorImpl(
161163
val docType = (format as? MsoMdocFormat)?.docType ?: (format as? SdJwtVcFormat)?.vct
162164
val docIdentifier = docType?.toDocumentIdentifier()
163165

164-
val shouldDeleteAllDocuments: Boolean =
165-
if (docIdentifier == DocumentIdentifier.MdocPid || docIdentifier == DocumentIdentifier.SdJwtPid) {
166+
val shouldDeleteAllDocuments: Boolean = if (configLogic.forcePidActivation
167+
&& (docIdentifier == DocumentIdentifier.MdocPid || docIdentifier == DocumentIdentifier.SdJwtPid)
168+
) {
166169

167-
val allPidDocuments = walletCoreDocumentsController.getAllDocumentsByType(
168-
documentIdentifiers = listOf(
169-
DocumentIdentifier.MdocPid,
170-
DocumentIdentifier.SdJwtPid
171-
)
170+
val allPidDocuments = walletCoreDocumentsController.getAllDocumentsByType(
171+
documentIdentifiers = listOf(
172+
DocumentIdentifier.MdocPid,
173+
DocumentIdentifier.SdJwtPid
172174
)
175+
)
173176

174-
if (allPidDocuments.count() > 1) {
175-
walletCoreDocumentsController.getMainPidDocument()?.id == documentId
176-
} else {
177-
true
178-
}
177+
if (allPidDocuments.count() > 1) {
178+
walletCoreDocumentsController.getMainPidDocument()?.id == documentId
179179
} else {
180-
false
180+
true
181181
}
182+
} else {
183+
false
184+
}
182185

183186
if (shouldDeleteAllDocuments) {
184187
walletCoreDocumentsController.deleteAllDocuments()

dashboard-feature/src/main/java/eu/europa/ec/dashboardfeature/interactor/DocumentsInteractor.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package eu.europa.ec.dashboardfeature.interactor
1818

19+
import eu.europa.ec.businesslogic.config.ConfigLogic
1920
import eu.europa.ec.businesslogic.extension.isBeyondNextDays
2021
import eu.europa.ec.businesslogic.extension.isExpired
2122
import eu.europa.ec.businesslogic.extension.isValid
@@ -169,6 +170,7 @@ class DocumentsInteractorImpl(
169170
private val resourceProvider: ResourceProvider,
170171
private val walletCoreDocumentsController: WalletCoreDocumentsController,
171172
private val filterValidator: FilterValidator,
173+
private val configLogic: ConfigLogic
172174
) : DocumentsInteractor {
173175

174176
private val genericErrorMsg
@@ -585,7 +587,9 @@ class DocumentsInteractorImpl(
585587
}
586588

587589
is DeleteDocumentPartialState.Success -> {
588-
if (walletCoreDocumentsController.getAllDocuments().isEmpty()) {
590+
if (configLogic.forcePidActivation
591+
&& walletCoreDocumentsController.getAllDocuments().isEmpty()
592+
) {
589593
emit(DocumentInteractorDeleteDocumentPartialState.AllDocumentsDeleted)
590594
} else
591595
emit(DocumentInteractorDeleteDocumentPartialState.SingleDocumentDeleted)

0 commit comments

Comments
 (0)