diff --git a/CATALOG.md b/CATALOG.md
index 632992e7..70f6cdc4 100644
--- a/CATALOG.md
+++ b/CATALOG.md
@@ -17,19 +17,26 @@ The minimal sample that includes all the required Backbase SDKs to get started t
\
LTS 24.03
+## [sample/devs/extending-a-journey](https://github.com/Backbase/golden-sample-app-android/tree/sample/devs/extending-a-journey)
+This example explains how to extend a journey.
+\
+**Status**: Maintained
+\
+LTS 24.09
+
## [sample/devs/O11Y](https://github.com/Backbase/golden-sample-app-android/tree/sample/devs/O11Y)
An implementation of Backbase Observability that makes it easy to add 3rd party tracking libraries to your project.
\
**Status**: Maintained
\
-LTS 24.03
+LTS 24.09
## [sample/devs/open-telemetry](https://github.com/Backbase/golden-sample-app-android/tree/sample/devs/open-telemetry)
An implementation of Backbase OpenTelemetry Connector to track screen views and user actions.
\
**Status**: Maintained
\
-LTS 24.03
+LTS 24.09
## [sample/devs/multi-theming](https://github.com/Backbase/golden-sample-app-android/tree/sample/devs/multi-theming)
This example explains how to use multiple themes in an application.
diff --git a/accounts-journey/src/main/kotlin/com/backbase/accounts_journey/presentation/accountdetail/ui/AccountDetailFragment.kt b/accounts-journey/src/main/kotlin/com/backbase/accounts_journey/presentation/accountdetail/ui/AccountDetailFragment.kt
index f986024c..fab2e6c0 100644
--- a/accounts-journey/src/main/kotlin/com/backbase/accounts_journey/presentation/accountdetail/ui/AccountDetailFragment.kt
+++ b/accounts-journey/src/main/kotlin/com/backbase/accounts_journey/presentation/accountdetail/ui/AccountDetailFragment.kt
@@ -10,8 +10,8 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
-import com.backbase.accounts_journey.R
import com.backbase.accounts_journey.databinding.FragmentAccountDetailBinding
+import com.backbase.accounts_journey.presentation.utils.UiUtils
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.koin.android.ext.android.inject
@@ -37,6 +37,8 @@ class AccountDetailFragment : Fragment() {
savedInstanceState: Bundle?
): View {
_binding = FragmentAccountDetailBinding.inflate(inflater, container, false)
+ UiUtils.applyWindowInsets(binding.contentMain)
+ UiUtils.applyWindowInsets(binding.toolbar)
return binding.root
}
diff --git a/accounts-journey/src/main/kotlin/com/backbase/accounts_journey/presentation/accountlist/ui/AccountListFragment.kt b/accounts-journey/src/main/kotlin/com/backbase/accounts_journey/presentation/accountlist/ui/AccountListFragment.kt
index 1b7c69e0..544905de 100644
--- a/accounts-journey/src/main/kotlin/com/backbase/accounts_journey/presentation/accountlist/ui/AccountListFragment.kt
+++ b/accounts-journey/src/main/kotlin/com/backbase/accounts_journey/presentation/accountlist/ui/AccountListFragment.kt
@@ -14,6 +14,7 @@ import com.backbase.accounts_journey.R
import com.backbase.accounts_journey.configuration.AccountsJourneyConfiguration
import com.backbase.accounts_journey.configuration.accountlist.AccountListScreenConfiguration
import com.backbase.accounts_journey.databinding.FragmentAccountListBinding
+import com.backbase.accounts_journey.router.AccountsRouter
import com.backbase.accounts_journey.routing.AccountsRouting
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -40,6 +41,8 @@ class AccountListFragment : Fragment() {
private val accountListAdapter: AccountListAdapter = AccountListAdapter(
onClick = { itemClicked(it) }
)
+ private val cardNavigationAction: AccountsRouter by inject()
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -75,6 +78,11 @@ class AccountListFragment : Fragment() {
.onEach { handleUiState(it) }
.launchIn(lifecycleScope)
viewModel.onEvent(AccountListEvent.OnGetAccounts)
+
+ // Remove this button and its action later
+ binding.btnCards.setOnClickListener {
+ cardNavigationAction.exit(findNavController())
+ }
}
private fun handleUiState(uiState: AccountListScreenState) {
diff --git a/accounts-journey/src/main/kotlin/com/backbase/accounts_journey/presentation/utils/UiUtils.kt b/accounts-journey/src/main/kotlin/com/backbase/accounts_journey/presentation/utils/UiUtils.kt
new file mode 100644
index 00000000..6c5ecfa1
--- /dev/null
+++ b/accounts-journey/src/main/kotlin/com/backbase/accounts_journey/presentation/utils/UiUtils.kt
@@ -0,0 +1,25 @@
+package com.backbase.accounts_journey.presentation.utils
+
+import android.view.View
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.updatePadding
+
+object UiUtils {
+
+ fun applyWindowInsets(view: View,) {
+ ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
+ val bars = insets.getInsets(
+ WindowInsetsCompat.Type.systemBars()
+ or WindowInsetsCompat.Type.displayCutout()
+ )
+ v.updatePadding(
+ left = bars.left,
+ top = bars.top,
+ right = bars.right,
+ bottom = bars.bottom,
+ )
+ WindowInsetsCompat.CONSUMED
+ }
+ }
+}
diff --git a/accounts-journey/src/main/kotlin/com/backbase/accounts_journey/router/AccountsRouter.kt b/accounts-journey/src/main/kotlin/com/backbase/accounts_journey/router/AccountsRouter.kt
new file mode 100644
index 00000000..f0fccb63
--- /dev/null
+++ b/accounts-journey/src/main/kotlin/com/backbase/accounts_journey/router/AccountsRouter.kt
@@ -0,0 +1,8 @@
+package com.backbase.accounts_journey.router
+
+import androidx.navigation.NavController
+
+interface AccountsRouter {
+
+ fun exit(navController: NavController)
+}
diff --git a/accounts-journey/src/main/res/layout/fragment_account_list.xml b/accounts-journey/src/main/res/layout/fragment_account_list.xml
index bbad7a60..9fe79920 100644
--- a/accounts-journey/src/main/res/layout/fragment_account_list.xml
+++ b/accounts-journey/src/main/res/layout/fragment_account_list.xml
@@ -103,6 +103,14 @@
android:visibility="gone"
app:constraint_referenced_ids="no_account_image,accounts_result_text"
tools:visibility="gone" />
+
+
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index ddb6d7f4..c63cf7a6 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -78,6 +78,9 @@ android {
dependencies {
implementation(projects.accountsJourney)
implementation(projects.accountsUseCase)
+ implementation(projects.cardJourney.impl)
+ implementation(projects.cardJourney.api)
+
implementation(platform(libs.kotlin.bom))
implementation(libs.bundles.android.core)
@@ -95,4 +98,5 @@ dependencies {
implementation(backbase.bundles.foundation)
implementation(backbase.bundles.journeys)
implementation(backbase.bundles.use.cases)
+
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 98b3198b..b7354758 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -14,7 +14,7 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:replace="android:fullBackupContent,android:theme"
- tools:targetApi="34">
+ tools:targetApi="35">
diff --git a/app/src/main/assets/backbase/config.json b/app/src/main/assets/backbase/config.json
index e0744b88..80c8f1b2 100644
--- a/app/src/main/assets/backbase/config.json
+++ b/app/src/main/assets/backbase/config.json
@@ -3,12 +3,12 @@
"debugEnable": true
},
"backbase": {
- "serverURL": "https://app.stg.sdbxaz.azure.backbaseservices.com",
+ "serverURL": "https://app.prd.sdbxaz.azure.backbaseservices.com",
"localModelPath": "$(contextRoot)/model.json",
"experience": "",
"version": "6.1.5",
"identity": {
- "baseURL": "https://identity.stg.sdbxaz.azure.backbaseservices.com",
+ "baseURL": "https://identity.prd.sdbxaz.azure.backbaseservices.com",
"realm": "customer",
"clientId": "mobile-client",
"applicationKey": "retail"
diff --git a/app/src/main/kotlin/com/backbase/golden_sample_app/MainApplication.kt b/app/src/main/kotlin/com/backbase/golden_sample_app/MainApplication.kt
index 6ea94c6e..52daf74b 100644
--- a/app/src/main/kotlin/com/backbase/golden_sample_app/MainApplication.kt
+++ b/app/src/main/kotlin/com/backbase/golden_sample_app/MainApplication.kt
@@ -12,12 +12,15 @@ import com.backbase.android.identity.fido.FidoUafFacetUtils
import com.backbase.android.identity.journey.authentication.initAuthenticationJourney
import com.backbase.android.identity.journey.authentication.stopAuthenticationJourney
import com.backbase.android.utils.net.NetworkConnectorBuilder
+import com.backbase.cards_journey.impl.cardsJourneyModule
+import com.backbase.cards_journey.impl.koin.cardModule
import com.backbase.golden_sample_app.authentication.CompositeSessionListener
import com.backbase.golden_sample_app.common.TAG
import com.backbase.golden_sample_app.koin.accountsModule
import com.backbase.golden_sample_app.koin.appModule
import com.backbase.golden_sample_app.koin.featureFilterModule
import com.backbase.golden_sample_app.koin.identityAuthModule
+import com.backbase.golden_sample_app.koin.navigationModule
import com.backbase.golden_sample_app.koin.presentationModule
import com.backbase.golden_sample_app.koin.securityModule
import com.backbase.golden_sample_app.koin.servicesModule
@@ -95,6 +98,7 @@ class MainApplication : Application() {
androidContext(this@MainApplication)
loadKoinModules(
listOf(
+ navigationModule,
securityModule(this@MainApplication),
servicesModule(this@MainApplication),
userModule(),
@@ -106,6 +110,8 @@ class MainApplication : Application() {
WorkspacesJourney.create(),
accountsModule,
AccountsJourney.create(configuration = setupAccountsJourneyConfiguration()),
+ cardModule(this@MainApplication),
+ cardsJourneyModule
)
)
}
diff --git a/app/src/main/kotlin/com/backbase/golden_sample_app/koin/NavigationModule.kt b/app/src/main/kotlin/com/backbase/golden_sample_app/koin/NavigationModule.kt
new file mode 100644
index 00000000..c8443bf4
--- /dev/null
+++ b/app/src/main/kotlin/com/backbase/golden_sample_app/koin/NavigationModule.kt
@@ -0,0 +1,27 @@
+package com.backbase.golden_sample_app.koin
+
+import androidx.navigation.NavController
+import com.backbase.accounts_journey.router.AccountsRouter
+import com.backbase.cards_journey.impl.CardsRouter
+import com.backbase.golden_sample_app.R
+import com.backbase.golden_sample_app.router.AppRouting
+import org.koin.dsl.module
+
+val navigationModule = module {
+ single {
+ object : AccountsRouter {
+ override fun exit(navController: NavController) {
+ navController.navigate(
+ R.id.action_global_cardsJourney,
+ )
+ }
+ }
+ }
+ factory {
+ object : CardsRouter {
+ override fun exit() {
+ get().getNavController()?.popBackStack()
+ }
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/backbase/golden_sample_app/presentation/MainActivity.kt b/app/src/main/kotlin/com/backbase/golden_sample_app/presentation/MainActivity.kt
index f4be237f..da443091 100644
--- a/app/src/main/kotlin/com/backbase/golden_sample_app/presentation/MainActivity.kt
+++ b/app/src/main/kotlin/com/backbase/golden_sample_app/presentation/MainActivity.kt
@@ -16,7 +16,6 @@ import com.backbase.golden_sample_app.R
import com.backbase.golden_sample_app.databinding.ActivityMainBinding
import com.backbase.golden_sample_app.menu.moreMenuModule
import com.backbase.golden_sample_app.presentation.bottom.setupBottomBar
-import com.backbase.golden_sample_app.presentation.header.updateStatusBarColor
import com.backbase.golden_sample_app.router.AppRouting
import com.backbase.golden_sample_app.session.sessionModule
import kotlinx.coroutines.flow.map
diff --git a/app/src/main/res/navigation/navigation_main.xml b/app/src/main/res/navigation/navigation_main.xml
index 911d01ba..c2dc5c79 100644
--- a/app/src/main/res/navigation/navigation_main.xml
+++ b/app/src/main/res/navigation/navigation_main.xml
@@ -55,4 +55,12 @@
android:name="com.backbase.golden_sample_app.presentation.header.UpComingJourneyFragment"
tools:layout="@layout/fragment_upcoming_journey" />
+
+
diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml
new file mode 100644
index 00000000..d7fbd2e0
--- /dev/null
+++ b/app/src/main/res/xml/network_security_config.xml
@@ -0,0 +1,23 @@
+
+
+
+ app.prd.sdbxaz.azure.backbaseservices.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/Version.kt b/buildSrc/src/main/kotlin/Version.kt
index aed2fae2..eadca0e8 100644
--- a/buildSrc/src/main/kotlin/Version.kt
+++ b/buildSrc/src/main/kotlin/Version.kt
@@ -1,5 +1,5 @@
object Version {
- const val compileSdk = 34
+ const val compileSdk = 35
const val minSdk = 26
const val versionCode = 1
const val versionName = "1.0.0"
diff --git a/buildSrc/src/main/kotlin/configured-detekt.gradle.kts b/buildSrc/src/main/kotlin/configured-detekt.gradle.kts
index 8269f112..31cca8d1 100644
--- a/buildSrc/src/main/kotlin/configured-detekt.gradle.kts
+++ b/buildSrc/src/main/kotlin/configured-detekt.gradle.kts
@@ -10,7 +10,7 @@ detekt {
toolVersion = "1.23.1"
buildUponDefaultConfig = true // preconfigure defaults
allRules = false // activate all available (even unstable) rules.
- config.setFrom("../config/golden-sample-app-detekt.yml") // point to your custom config defining rules to run, overwriting default behavior
+ config.setFrom("$rootDir/config/golden-sample-app-detekt.yml") // point to your custom config defining rules to run, overwriting default behavior
parallel = true
autoCorrect = true
ignoredVariants = listOf("release")
diff --git a/card-journey/api/.gitignore b/card-journey/api/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/card-journey/api/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/card-journey/api/build.gradle.kts b/card-journey/api/build.gradle.kts
new file mode 100644
index 00000000..98c7e127
--- /dev/null
+++ b/card-journey/api/build.gradle.kts
@@ -0,0 +1,17 @@
+plugins {
+ id(backbase.plugins.feature.android.module.get().pluginId)
+ id(libs.plugins.kotlin.parcelize.get().pluginId)
+ id(libs.plugins.navigation.safe.args.get().pluginId)
+ id(backbase.plugins.configured.detekt.get().pluginId)
+}
+
+android {
+ namespace = "com.backbase.cards_journey.api"
+}
+
+dependencies {
+
+ // Backbase libraries
+ implementation(backbase.bom)
+ implementation(backbase.bundles.card.client.common)
+}
\ No newline at end of file
diff --git a/card-journey/api/src/main/java/com/backbase/api/data/CustomCardClient.kt b/card-journey/api/src/main/java/com/backbase/api/data/CustomCardClient.kt
new file mode 100644
index 00000000..14dd6135
--- /dev/null
+++ b/card-journey/api/src/main/java/com/backbase/api/data/CustomCardClient.kt
@@ -0,0 +1,9 @@
+package com.backbase.api.data
+
+import com.backbase.android.client.cardsclient2.model.CardItem
+import com.backbase.android.clients.common.Call
+
+interface CustomCardClient {
+ fun getCards(): Call>
+ fun getCardDetails(id: String): Call
+}
diff --git a/card-journey/api/src/main/java/com/backbase/api/domain/CustomCardUseCase.kt b/card-journey/api/src/main/java/com/backbase/api/domain/CustomCardUseCase.kt
new file mode 100644
index 00000000..e4a52d41
--- /dev/null
+++ b/card-journey/api/src/main/java/com/backbase/api/domain/CustomCardUseCase.kt
@@ -0,0 +1,9 @@
+package com.backbase.api.domain
+
+import com.backbase.android.client.cardsclient2.model.CardItem
+
+interface CustomCardUseCase {
+
+ suspend fun getCards(): Result>
+ suspend fun getCardDetails(id: String): Result
+}
diff --git a/card-journey/impl/.gitignore b/card-journey/impl/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/card-journey/impl/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/card-journey/impl/build.gradle.kts b/card-journey/impl/build.gradle.kts
new file mode 100644
index 00000000..7942e1c8
--- /dev/null
+++ b/card-journey/impl/build.gradle.kts
@@ -0,0 +1,20 @@
+plugins {
+ id(backbase.plugins.feature.android.module.get().pluginId)
+ id(libs.plugins.kotlin.parcelize.get().pluginId)
+ id(libs.plugins.navigation.safe.args.get().pluginId)
+ id(backbase.plugins.configured.detekt.get().pluginId)
+}
+
+android {
+ namespace = "com.backbase.cards_journey.impl"
+}
+
+dependencies {
+ // Backbase libraries
+ implementation(backbase.bom)
+ implementation(backbase.bundles.common)
+ implementation(libs.bundles.navigation)
+ implementation(backbase.bundles.card.client.common)
+ implementation(projects.cardJourney.api)
+ implementation(projects.coreUi)
+}
\ No newline at end of file
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/CardsJourney.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/CardsJourney.kt
new file mode 100644
index 00000000..6f4098c6
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/CardsJourney.kt
@@ -0,0 +1,30 @@
+package com.backbase.cards_journey.impl
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import com.backbase.cards_journey.impl.databinding.FragmentCardsJourneyBinding
+
+class CardsJourney : Fragment() {
+ private val scope by viewModels()
+
+ private var _binding: FragmentCardsJourneyBinding? = null
+ private val binding get() = _binding!!
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ scope.launchWithArguments()
+ _binding = FragmentCardsJourneyBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ }
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/CardsJourneyModule.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/CardsJourneyModule.kt
new file mode 100644
index 00000000..f12909a8
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/CardsJourneyModule.kt
@@ -0,0 +1,8 @@
+package com.backbase.cards_journey.impl
+
+import org.koin.dsl.module
+
+val cardsJourneyModule = module {
+ scope {
+ }
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/CardsJourneyScope.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/CardsJourneyScope.kt
new file mode 100644
index 00000000..42b29026
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/CardsJourneyScope.kt
@@ -0,0 +1,63 @@
+package com.backbase.cards_journey.impl
+
+import androidx.annotation.MainThread
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModel
+import com.backbase.android.retail.journey.koin.KoinScopeViewModel
+import com.backbase.android.retail.journey.koin.scoped
+import com.backbase.android.retail.journey.koin.scopedViewModel
+import com.backbase.cards_journey.impl.ui.details.CardDetailsViewModel
+import com.backbase.cards_journey.impl.ui.list.CardListViewModel
+import org.koin.androidx.viewmodel.dsl.viewModel
+import org.koin.core.context.loadKoinModules
+import org.koin.core.context.unloadKoinModules
+import org.koin.core.module.Module
+import org.koin.core.parameter.ParametersDefinition
+import org.koin.core.qualifier.Qualifier
+import org.koin.dsl.module
+
+interface CardsJourneyScope
+
+@MainThread
+internal inline fun Fragment.journeyScoped(
+ qualifier: Qualifier? = null,
+ noinline parameters: ParametersDefinition? = null
+): Lazy =
+ scoped(qualifier, parameters)
+
+@MainThread
+internal inline fun Fragment.journeyScopedViewModel(
+ qualifier: Qualifier? = null,
+ noinline parameters: ParametersDefinition? = null
+): Lazy =
+ scopedViewModel(
+ qualifier,
+ parameters
+ )
+
+class CardsJourneyScopeImpl :
+ KoinScopeViewModel(CardsJourneyScope::class, module { }),
+ CardsJourneyScope {
+
+ private var scopeModule: Module? = null
+
+ fun launchWithArguments() {
+ if (scopeModule == null) {
+ scopeModule = ScopedModule {
+ viewModel {
+ CardListViewModel(get())
+ }
+ viewModel { (id: String) ->
+ CardDetailsViewModel(cardId = id, get())
+ }
+ }.apply {
+ loadKoinModules(this)
+ }
+ }
+ }
+
+ override fun onCleared() {
+ super.onCleared()
+ scopeModule?.let { unloadKoinModules(it) }
+ }
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/CardsRouter.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/CardsRouter.kt
new file mode 100644
index 00000000..90da8bd8
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/CardsRouter.kt
@@ -0,0 +1,5 @@
+package com.backbase.cards_journey.impl
+
+interface CardsRouter {
+ fun exit()
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/data/CustomCardClientImpl.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/data/CustomCardClientImpl.kt
new file mode 100644
index 00000000..7122929a
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/data/CustomCardClientImpl.kt
@@ -0,0 +1,54 @@
+package com.backbase.cards_journey.impl.data
+
+import android.content.Context
+import com.backbase.android.Backbase
+import com.backbase.android.client.cardsclient2.model.CardItem
+import com.backbase.android.clients.common.Call
+import com.backbase.android.clients.common.MoshiResponseBodyParser
+import com.backbase.android.common.utils.dbs.Params
+import com.backbase.android.dbs.DBSDataProvider
+import com.backbase.api.data.CustomCardClient
+import com.backbase.cards_journey.impl.utils.BBDbsClient
+import com.backbase.cards_journey.impl.utils.Constants
+import com.squareup.moshi.Types
+import java.net.URI
+
+class CustomCardClientImpl(
+ private val parser: MoshiResponseBodyParser,
+ context: Context,
+ serverUri: URI,
+ provider: DBSDataProvider,
+ backbase: Backbase
+) : BBDbsClient(context, serverUri, provider, backbase), CustomCardClient {
+ override fun getCards(): Call> {
+ return buildGetRequest(
+ serverUri,
+ Constants.GET_CARDS_END_POINT,
+ Params(),
+ provider
+ ).let {
+ Call(
+ it,
+ provider,
+ parser,
+ Types.newParameterizedType(List::class.java, CardItem::class.java)
+ )
+ }
+ }
+
+ override fun getCardDetails(id: String): Call {
+ return buildGetRequest(
+ serverUri,
+ Constants.GET_CARD_DETAILS_END_POINT + id,
+ Params(),
+ provider
+ ).let {
+ Call(
+ it,
+ provider,
+ parser,
+ CardItem::class.java
+ )
+ }
+ }
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/domain/CustomCardUseCaseImpl.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/domain/CustomCardUseCaseImpl.kt
new file mode 100644
index 00000000..f962382c
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/domain/CustomCardUseCaseImpl.kt
@@ -0,0 +1,63 @@
+package com.backbase.cards_journey.impl.domain
+
+import com.backbase.android.client.cardsclient2.model.CardItem
+import com.backbase.android.clients.common.CallResult
+import com.backbase.android.clients.common.coroutines.executeAsSuspended
+import com.backbase.api.data.CustomCardClient
+import com.backbase.api.domain.CustomCardUseCase
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+class CustomCardUseCaseImpl(private val customCardClient: CustomCardClient) : CustomCardUseCase {
+ override suspend fun getCards(): Result> {
+ return withContext(Dispatchers.IO) {
+ when (
+ val callResult =
+ customCardClient.getCards().executeAsSuspended()
+ ) {
+ is CallResult.Success -> {
+ Result.success(callResult.data)
+ }
+
+ is CallResult.Error -> {
+ Result.failure(
+ Throwable(
+ message = callResult.errorResponse.errorMessage,
+ cause = Throwable(callResult.errorResponse.causeTrace),
+ )
+ )
+ }
+
+ else -> {
+ Result.failure(Throwable())
+ }
+ }
+ }
+ }
+
+ override suspend fun getCardDetails(id: String): Result {
+ return withContext(Dispatchers.IO) {
+ when (
+ val callResult =
+ customCardClient.getCardDetails(id).executeAsSuspended()
+ ) {
+ is CallResult.Success -> {
+ Result.success(callResult.data)
+ }
+
+ is CallResult.Error -> {
+ Result.failure(
+ Throwable(
+ message = callResult.errorResponse.errorMessage,
+ cause = Throwable(callResult.errorResponse.causeTrace),
+ )
+ )
+ }
+
+ else -> {
+ Result.failure(Throwable())
+ }
+ }
+ }
+ }
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/koin/CardModule.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/koin/CardModule.kt
new file mode 100644
index 00000000..1aee5f17
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/koin/CardModule.kt
@@ -0,0 +1,45 @@
+package com.backbase.cards_journey.impl.koin
+
+import android.content.Context
+import com.backbase.android.Backbase
+import com.backbase.android.clients.common.MoshiResponseBodyParser
+import com.backbase.android.clients.common.base64Adapter
+import com.backbase.android.clients.common.bigDecimalAdapter
+import com.backbase.android.clients.common.dateAdapter
+import com.backbase.android.clients.common.dateTimeAdapter
+import com.backbase.android.dbs.dataproviders.NetworkDBSDataProvider
+import com.backbase.api.data.CustomCardClient
+import com.backbase.api.domain.CustomCardUseCase
+import com.backbase.cards_journey.impl.domain.CustomCardUseCaseImpl
+import com.squareup.moshi.Moshi
+import org.koin.dsl.module
+import java.net.URI
+
+val serverUrl: String?
+ get() = Backbase.getInstance()?.configuration?.experienceConfiguration?.serverURL
+private const val CARD_MANAGER_ENDPOINT = "/api/cards-presentation-service"
+private val moshi = Moshi.Builder()
+ .add(bigDecimalAdapter)
+ .add(dateAdapter)
+ .add(dateTimeAdapter)
+ .add(base64Adapter)
+ .build()
+
+fun cardModule(context: Context) = module {
+ val backbase = Backbase.requireInstance()
+ single { backbase.getClient(com.backbase.cards_journey.impl.data.CustomCardClientImpl::class.java) }
+ factory {
+ CustomCardUseCaseImpl(
+ customCardClient = get()
+ )
+ }
+ factory {
+ com.backbase.cards_journey.impl.data.CustomCardClientImpl(
+ context = context,
+ parser = MoshiResponseBodyParser(moshi),
+ serverUri = URI(serverUrl + CARD_MANAGER_ENDPOINT),
+ provider = NetworkDBSDataProvider(context),
+ backbase = Backbase.requireInstance()
+ )
+ }
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/details/CardDetailsFragment.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/details/CardDetailsFragment.kt
new file mode 100644
index 00000000..02de28b1
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/details/CardDetailsFragment.kt
@@ -0,0 +1,98 @@
+package com.backbase.cards_journey.impl.ui.details
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.core.view.isVisible
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.fragment.navArgs
+import com.backbase.android.client.cardsclient2.model.CardItem
+import com.backbase.cards_journey.impl.databinding.FragmentCardDetailsBinding
+import com.backbase.cards_journey.impl.journeyScopedViewModel
+import com.backbase.cards_journey.impl.utils.getMaskedText
+import kotlinx.coroutines.launch
+import org.koin.core.parameter.parametersOf
+
+class CardDetailsFragment : Fragment() {
+
+ private var _binding: FragmentCardDetailsBinding? = null
+ private val args: CardDetailsFragmentArgs by navArgs()
+ private val viewModel by journeyScopedViewModel(
+ parameters = { parametersOf(args.cardId) }
+ )
+ private val binding get() = _binding!!
+
+ init {
+ collectState()
+ collectEffect()
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentCardDetailsBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ val params = FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ )
+ params.setMargins(0, 0, 0, 0)
+ binding.card.cardLayout.layoutParams = params
+ binding.toolbar.setNavigationOnClickListener {
+ findNavController().navigateUp()
+ }
+ viewModel.sendEvent(CardDetailsScreenEvent.GetCardDetails)
+ }
+
+ private fun collectState() {
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.state.collect { state ->
+ binding.loading.isVisible = state.isLoading
+ applyCardDetails(state.card)
+ }
+ }
+ }
+ }
+
+ private fun collectEffect() {
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.effect.collect { effect ->
+ when (effect) {
+ CardDetailsScreenEffect.HandleErrorResponse ->
+ binding.error.isVisible =
+ true
+ }
+ }
+ }
+ }
+ }
+
+ private fun applyCardDetails(card: CardItem?) {
+ binding.details.isVisible = card != null
+ if (card != null) {
+ binding.error.isVisible = false
+ binding.apply {
+ subType.text = card.subType
+ currency.text = card.currency
+ onlineLimit.text = card.limits?.getOrNull(0)?.amount?.toString()
+ atmLimit.text = card.limits?.getOrNull(1)?.amount?.toString()
+ binding.card.title.text = card.name
+ binding.card.cardNumber.text = getMaskedText(card)
+ }
+ }
+ }
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/details/CardDetailsScreenContract.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/details/CardDetailsScreenContract.kt
new file mode 100644
index 00000000..d94d22d7
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/details/CardDetailsScreenContract.kt
@@ -0,0 +1,19 @@
+package com.backbase.cards_journey.impl.ui.details
+
+import com.backbase.android.client.cardsclient2.model.CardItem
+import com.backbase.core_ui.mvi.mvi.UiEffect
+import com.backbase.core_ui.mvi.mvi.UiEvent
+import com.backbase.core_ui.mvi.mvi.UiState
+
+data class CardDetailsScreenViewState(
+ val isLoading: Boolean,
+ val card: CardItem? = null
+) : UiState()
+
+sealed class CardDetailsScreenEffect : UiEffect() {
+ data object HandleErrorResponse : CardDetailsScreenEffect()
+}
+
+sealed class CardDetailsScreenEvent : UiEvent() {
+ data object GetCardDetails : CardDetailsScreenEvent()
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/details/CardDetailsViewModel.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/details/CardDetailsViewModel.kt
new file mode 100644
index 00000000..c4874ff5
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/details/CardDetailsViewModel.kt
@@ -0,0 +1,41 @@
+package com.backbase.cards_journey.impl.ui.details
+
+import androidx.lifecycle.viewModelScope
+import com.backbase.api.domain.CustomCardUseCase
+import com.backbase.core_ui.mvi.mvi.BaseViewModel
+import kotlinx.coroutines.launch
+
+class CardDetailsViewModel(val cardId: String, private val useCase: CustomCardUseCase) :
+ BaseViewModel() {
+ override fun handleEvent(event: CardDetailsScreenEvent) {
+ when (event) {
+ CardDetailsScreenEvent.GetCardDetails -> getCardDetails()
+ }
+ }
+
+ override fun createInitialState(): CardDetailsScreenViewState {
+ return CardDetailsScreenViewState(false)
+ }
+
+ private fun getCardDetails() {
+ setState {
+ copy(isLoading = true)
+ }
+ viewModelScope.launch {
+ useCase.getCardDetails(cardId).fold(
+ onSuccess = { result ->
+ setState {
+ copy(
+ isLoading = false,
+ card = result
+ )
+ }
+ },
+ onFailure = {
+ setState { copy(isLoading = false) }
+ sendEffect(CardDetailsScreenEffect.HandleErrorResponse)
+ }
+ )
+ }
+ }
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardListAdapter.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardListAdapter.kt
new file mode 100644
index 00000000..0424e1fc
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardListAdapter.kt
@@ -0,0 +1,38 @@
+package com.backbase.cards_journey.impl.ui.list
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import com.backbase.android.client.cardsclient2.model.CardItem
+import com.backbase.cards_journey.impl.databinding.InflateCardBinding
+
+class CardListAdapter(val onclick: (CardItem) -> Unit) :
+ ListAdapter(BillsDiffCallBack()) {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
+ return CardViewHolder(
+ InflateCardBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ )
+ }
+
+ override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
+ holder.bind(getItem(position), onclick)
+ }
+
+ private class BillsDiffCallBack : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(
+ oldItem: CardItem,
+ newItem: CardItem
+ ): Boolean = oldItem.id == newItem.id
+
+ override fun areContentsTheSame(
+ oldItem: CardItem,
+ newItem: CardItem
+ ): Boolean = oldItem == newItem
+ }
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardListFragment.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardListFragment.kt
new file mode 100644
index 00000000..b5ea7562
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardListFragment.kt
@@ -0,0 +1,91 @@
+package com.backbase.cards_journey.impl.ui.list
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.navigation.fragment.findNavController
+import androidx.viewpager2.widget.ViewPager2
+import com.backbase.android.client.cardsclient2.model.CardItem
+import com.backbase.cards_journey.impl.CardsRouter
+import com.backbase.cards_journey.impl.databinding.FragmentCardListBinding
+import com.backbase.cards_journey.impl.journeyScopedViewModel
+import kotlinx.coroutines.launch
+import org.koin.android.ext.android.inject
+
+class CardListFragment : Fragment() {
+
+ private val viewModel by journeyScopedViewModel()
+ private val rounter by inject()
+
+ private var _binding: FragmentCardListBinding? = null
+ private val binding get() = _binding!!
+ private lateinit var cardsAdapter: CardListAdapter
+
+ init {
+ collectState()
+ collectEffect()
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentCardListBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ viewModel.sendEvent(CardListScreenEvent.GetCardList)
+
+ cardsAdapter = CardListAdapter(::onclick)
+ binding.pager.apply {
+ orientation = ViewPager2.ORIENTATION_HORIZONTAL
+ offscreenPageLimit = 2
+ adapter = cardsAdapter
+ }
+ binding.toolbar.setNavigationOnClickListener {
+ rounter.exit()
+ }
+ }
+
+ private fun onclick(card: CardItem) {
+ findNavController()
+ .navigate(CardListFragmentDirections.actionCardListFragmentToCardDetailsFragment(card.id))
+ }
+
+ private fun collectState() {
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.state.collect { state ->
+ binding.loading.isVisible = state.isLoading
+ if (state.cards != null) {
+ binding.error.isVisible = false
+ cardsAdapter.submitList(state.cards)
+ }
+ }
+ }
+ }
+ }
+
+ private fun collectEffect() {
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.effect.collect { effect ->
+ when (effect) {
+ CardListScreenEffect.HandleErrorResponse ->
+ binding.error.isVisible =
+ true
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardListScreenContract.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardListScreenContract.kt
new file mode 100644
index 00000000..ecd20670
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardListScreenContract.kt
@@ -0,0 +1,19 @@
+package com.backbase.cards_journey.impl.ui.list
+
+import com.backbase.android.client.cardsclient2.model.CardItem
+import com.backbase.core_ui.mvi.mvi.UiEffect
+import com.backbase.core_ui.mvi.mvi.UiEvent
+import com.backbase.core_ui.mvi.mvi.UiState
+
+data class CardListScreenViewState(
+ val isLoading: Boolean,
+ val cards: List? = null
+) : UiState()
+
+sealed class CardListScreenEffect : UiEffect() {
+ data object HandleErrorResponse : CardListScreenEffect()
+}
+
+sealed class CardListScreenEvent : UiEvent() {
+ data object GetCardList : CardListScreenEvent()
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardListViewModel.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardListViewModel.kt
new file mode 100644
index 00000000..021069f5
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardListViewModel.kt
@@ -0,0 +1,43 @@
+package com.backbase.cards_journey.impl.ui.list
+
+import androidx.lifecycle.viewModelScope
+import com.backbase.api.domain.CustomCardUseCase
+import com.backbase.core_ui.mvi.mvi.BaseViewModel
+import kotlinx.coroutines.launch
+
+class CardListViewModel(private val useCase: CustomCardUseCase) :
+ BaseViewModel() {
+ override fun handleEvent(event: CardListScreenEvent) {
+ when (event) {
+ CardListScreenEvent.GetCardList -> getCardList()
+ }
+ }
+
+ override fun createInitialState(): CardListScreenViewState {
+ return CardListScreenViewState(false)
+ }
+
+ private fun getCardList() {
+ if (state.value.cards != null) return
+ setState {
+ copy(isLoading = true)
+ }
+
+ viewModelScope.launch {
+ useCase.getCards().fold(
+ onSuccess = { result ->
+ setState {
+ copy(
+ isLoading = false,
+ cards = result
+ )
+ }
+ },
+ onFailure = {
+ setState { copy(isLoading = false) }
+ sendEffect(CardListScreenEffect.HandleErrorResponse)
+ }
+ )
+ }
+ }
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardViewHolder.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardViewHolder.kt
new file mode 100644
index 00000000..141b4a4b
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/ui/list/CardViewHolder.kt
@@ -0,0 +1,19 @@
+package com.backbase.cards_journey.impl.ui.list
+
+import androidx.recyclerview.widget.RecyclerView
+import com.backbase.android.client.cardsclient2.model.CardItem
+import com.backbase.cards_journey.impl.databinding.InflateCardBinding
+import com.backbase.cards_journey.impl.utils.getMaskedText
+
+class CardViewHolder(private val binding: InflateCardBinding) :
+ RecyclerView.ViewHolder(binding.root) {
+ fun bind(item: CardItem?, onclick: (CardItem) -> Unit) {
+ binding.title.text = item?.name
+ binding.cardNumber.text = getMaskedText(item)
+ binding.root.setOnClickListener {
+ if (item != null) {
+ onclick(item)
+ }
+ }
+ }
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/utils/AppUtils.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/utils/AppUtils.kt
new file mode 100644
index 00000000..8ad90a47
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/utils/AppUtils.kt
@@ -0,0 +1,14 @@
+package com.backbase.cards_journey.impl.utils
+
+import com.backbase.android.client.cardsclient2.model.CardItem
+
+object Constants {
+ const val GET_CARDS_END_POINT = "/client-api/v2/cards"
+ const val GET_CARD_DETAILS_END_POINT = "/client-api/v2/cards/"
+}
+
+fun getMaskedText(item: CardItem?): String? {
+ return item?.let {
+ "**** **** **** ${it.maskedNumber}"
+ }
+}
diff --git a/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/utils/BBDbsClient.kt b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/utils/BBDbsClient.kt
new file mode 100644
index 00000000..cda8a507
--- /dev/null
+++ b/card-journey/impl/src/main/java/com/backbase/cards_journey/impl/utils/BBDbsClient.kt
@@ -0,0 +1,28 @@
+package com.backbase.cards_journey.impl.utils
+
+import android.content.Context
+import com.backbase.android.Backbase
+import com.backbase.android.common.utils.dbs.CustomNetworkDBSProvider
+import com.backbase.android.common.utils.dbs.DBSDataImpl
+import com.backbase.android.dbs.DBSClient
+import com.backbase.android.dbs.DBSDataProvider
+import java.net.URI
+
+open class BBDbsClient(
+ val context: Context,
+ var serverUri: URI,
+ var provider: DBSDataProvider = CustomNetworkDBSProvider(context),
+ var backbase: Backbase = requireNotNull(Backbase.getInstance()) { "The Backbase instance must not be null!" }
+) : DBSClient, DBSDataImpl(backbase) {
+ override fun setBaseURI(baseUri: URI) {
+ serverUri = baseUri
+ }
+
+ override fun getBaseURI() = this.serverUri
+
+ override fun setDataProvider(provider: DBSDataProvider?) {
+ this.provider = requireNotNull(provider) { "The provider must not be null!" }
+ }
+
+ override fun getDataProvider(): DBSDataProvider = provider
+}
diff --git a/card-journey/impl/src/main/res/drawable/card_gradient.xml b/card-journey/impl/src/main/res/drawable/card_gradient.xml
new file mode 100644
index 00000000..e588df76
--- /dev/null
+++ b/card-journey/impl/src/main/res/drawable/card_gradient.xml
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/card-journey/impl/src/main/res/drawable/ic_back.xml b/card-journey/impl/src/main/res/drawable/ic_back.xml
new file mode 100644
index 00000000..8452791c
--- /dev/null
+++ b/card-journey/impl/src/main/res/drawable/ic_back.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/card-journey/impl/src/main/res/drawable/ic_backbase.xml b/card-journey/impl/src/main/res/drawable/ic_backbase.xml
new file mode 100644
index 00000000..7990753d
--- /dev/null
+++ b/card-journey/impl/src/main/res/drawable/ic_backbase.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/card-journey/impl/src/main/res/drawable/ic_mada_logo.xml b/card-journey/impl/src/main/res/drawable/ic_mada_logo.xml
new file mode 100644
index 00000000..68fc1a9d
--- /dev/null
+++ b/card-journey/impl/src/main/res/drawable/ic_mada_logo.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/card-journey/impl/src/main/res/drawable/ic_mc_logo.xml b/card-journey/impl/src/main/res/drawable/ic_mc_logo.xml
new file mode 100644
index 00000000..6dcd995a
--- /dev/null
+++ b/card-journey/impl/src/main/res/drawable/ic_mc_logo.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/card-journey/impl/src/main/res/layout/fragment_card_details.xml b/card-journey/impl/src/main/res/layout/fragment_card_details.xml
new file mode 100644
index 00000000..4f49d26a
--- /dev/null
+++ b/card-journey/impl/src/main/res/layout/fragment_card_details.xml
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/card-journey/impl/src/main/res/layout/fragment_card_list.xml b/card-journey/impl/src/main/res/layout/fragment_card_list.xml
new file mode 100644
index 00000000..09d3f65c
--- /dev/null
+++ b/card-journey/impl/src/main/res/layout/fragment_card_list.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/card-journey/impl/src/main/res/layout/fragment_cards_journey.xml b/card-journey/impl/src/main/res/layout/fragment_cards_journey.xml
new file mode 100644
index 00000000..33e586e1
--- /dev/null
+++ b/card-journey/impl/src/main/res/layout/fragment_cards_journey.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/card-journey/impl/src/main/res/layout/inflate_card.xml b/card-journey/impl/src/main/res/layout/inflate_card.xml
new file mode 100644
index 00000000..0e0a9285
--- /dev/null
+++ b/card-journey/impl/src/main/res/layout/inflate_card.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/card-journey/impl/src/main/res/navigation/cards_navigation.xml b/card-journey/impl/src/main/res/navigation/cards_navigation.xml
new file mode 100644
index 00000000..7fbb7c16
--- /dev/null
+++ b/card-journey/impl/src/main/res/navigation/cards_navigation.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/card-journey/impl/src/main/res/values/dimens.xml b/card-journey/impl/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..a0e53c06
--- /dev/null
+++ b/card-journey/impl/src/main/res/values/dimens.xml
@@ -0,0 +1,9 @@
+
+
+ 16dp
+ 8dp
+ 100dp
+ 200dp
+ 150dp
+ 50dp
+
\ No newline at end of file
diff --git a/card-journey/impl/src/main/res/values/strings.xml b/card-journey/impl/src/main/res/values/strings.xml
new file mode 100644
index 00000000..273a79dd
--- /dev/null
+++ b/card-journey/impl/src/main/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+ Error occurred
+ Sub Type
+ Currency
+ Online limit
+ Atm limit
+ Details
+ Cards
+
\ No newline at end of file
diff --git a/card-journey/impl/src/main/res/values/styles.xml b/card-journey/impl/src/main/res/values/styles.xml
new file mode 100644
index 00000000..fe8d573c
--- /dev/null
+++ b/card-journey/impl/src/main/res/values/styles.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core-ui/.gitignore b/core-ui/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/core-ui/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/core-ui/build.gradle.kts b/core-ui/build.gradle.kts
new file mode 100644
index 00000000..6400a21e
--- /dev/null
+++ b/core-ui/build.gradle.kts
@@ -0,0 +1,12 @@
+plugins {
+ id(backbase.plugins.base.android.library.module.get().pluginId)
+ id(backbase.plugins.configured.detekt.get().pluginId)
+}
+
+android {
+ namespace = "com.backbase.core_ui"
+}
+
+dependencies {
+ implementation(libs.bundles.android.core)
+}
diff --git a/core-ui/src/main/java/com/backbase/core_ui/mvi/mvi/BaseViewModel.kt b/core-ui/src/main/java/com/backbase/core_ui/mvi/mvi/BaseViewModel.kt
new file mode 100644
index 00000000..bbd33660
--- /dev/null
+++ b/core-ui/src/main/java/com/backbase/core_ui/mvi/mvi/BaseViewModel.kt
@@ -0,0 +1,57 @@
+package com.backbase.core_ui.mvi.mvi
+
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.launch
+
+abstract class BaseViewModel : ViewModel() {
+
+ protected val currentState: State
+ get() = _state.value
+
+ @Suppress("VariableNaming")
+ @VisibleForTesting
+ val _state = MutableStateFlow(createInitialState())
+ val state: StateFlow = _state.asStateFlow()
+
+ @Suppress("VariableNaming")
+ protected val _event: MutableSharedFlow = MutableSharedFlow()
+ private val event: SharedFlow = _event.asSharedFlow()
+
+ @Suppress("VariableNaming")
+ private val _effect: Channel = Channel()
+ val effect = _effect.receiveAsFlow()
+
+ init {
+ viewModelScope.launch {
+ event.collect { handleEvent(it) }
+ }
+ }
+
+ fun sendEvent(event: Event) {
+ viewModelScope.launch { _event.emit(event) }
+ }
+
+ protected fun setState(reduce: State.() -> State) {
+ _state.value = currentState.reduce()
+ }
+
+ protected fun sendEffect(effect: Effect) {
+ viewModelScope.launch { _effect.send(effect) }
+ }
+
+ protected fun launch(block: suspend () -> Unit) = viewModelScope.launch { block() }
+
+ abstract fun handleEvent(event: Event)
+
+ protected abstract fun createInitialState(): State
+}
diff --git a/core-ui/src/main/java/com/backbase/core_ui/mvi/mvi/UiEffect.kt b/core-ui/src/main/java/com/backbase/core_ui/mvi/mvi/UiEffect.kt
new file mode 100644
index 00000000..8cf4d41d
--- /dev/null
+++ b/core-ui/src/main/java/com/backbase/core_ui/mvi/mvi/UiEffect.kt
@@ -0,0 +1,3 @@
+package com.backbase.core_ui.mvi.mvi
+
+open class UiEffect
diff --git a/core-ui/src/main/java/com/backbase/core_ui/mvi/mvi/UiEvent.kt b/core-ui/src/main/java/com/backbase/core_ui/mvi/mvi/UiEvent.kt
new file mode 100644
index 00000000..95fa8e01
--- /dev/null
+++ b/core-ui/src/main/java/com/backbase/core_ui/mvi/mvi/UiEvent.kt
@@ -0,0 +1,3 @@
+package com.backbase.core_ui.mvi.mvi
+
+open class UiEvent
diff --git a/core-ui/src/main/java/com/backbase/core_ui/mvi/mvi/UiState.kt b/core-ui/src/main/java/com/backbase/core_ui/mvi/mvi/UiState.kt
new file mode 100644
index 00000000..c84f8f62
--- /dev/null
+++ b/core-ui/src/main/java/com/backbase/core_ui/mvi/mvi/UiState.kt
@@ -0,0 +1,3 @@
+package com.backbase.core_ui.mvi.mvi
+
+open class UiState
diff --git a/gradle/backbase.versions.toml b/gradle/backbase.versions.toml
index 9c4c5352..f347a103 100644
--- a/gradle/backbase.versions.toml
+++ b/gradle/backbase.versions.toml
@@ -1,10 +1,16 @@
[versions]
## A
access-control-client-2-entitlements-use-case = "6.0.0"
-authentication-journey = "7.1.0"
+authentication-journey = "7.2.0"
## B
-bom = "2024.07.01"
+bom = "2024.09.01"
+
+## C
+clients-common-coroutines = "2.0.0"
+cards-client = "2.0.0"
+clients-common = "1.6.2"
+common-utils = "7.0.0"
## F
feature-filter-use-case = "6.0.0"
@@ -27,6 +33,9 @@ business-journey-common = { group = "com.backbase.android.business.journey", nam
## C
clients-common-coroutines = { group = "com.backbase.android.clients", name = "clients-common-coroutines", version = "" }
+cards-client = { group = "com.backbase.android.clients", name = "gen-cards-client-2", version.ref = "cards-client" }
+clients-common = { group = "com.backbase.android.clients", name = "clients-common", version.ref = "clients-common" }
+common-utils = { group = "com.backbase.android.common", name = "common-utils", version.ref = "common-utils" }
## D
design-system = { group = "com.backbase.android.design", name = "design-system", version = "" }
@@ -87,6 +96,13 @@ common = [
"retail-journey-common-koin",
"journey-test",
]
+card-client-common = [
+ "cards-client",
+ "clients-common-coroutines",
+ "clients-common",
+ "common-utils",
+ "retail-journey-common",
+]
## F
foundation = [
diff --git a/keystore/debug.keystore b/keystore/debug.keystore
new file mode 100644
index 00000000..3dc75013
Binary files /dev/null and b/keystore/debug.keystore differ
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 8f3498a8..ffa76a0c 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -34,3 +34,6 @@ include(":app")
include(":accounts-journey")
include(":accounts-use-case")
include(":fake-accounts-use-case")
+include(":card-journey:impl")
+include(":card-journey:api")
+include(":core-ui")