Updated on 2026-08-14

This commit is contained in:
Tangem 2024-11-26 15:39:51 +04:00
commit f6a56e1657
64 changed files with 388 additions and 226 deletions

View file

@ -81,7 +81,7 @@ dependencies {
implementation(projects.core.analytics) implementation(projects.core.analytics)
implementation(projects.core.analytics.models) implementation(projects.core.analytics.models)
implementation(projects.core.navigation) implementation(projects.core.navigation)
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.res) implementation(projects.core.res)
implementation(projects.core.ui) implementation(projects.core.ui)
implementation(projects.core.datasource) implementation(projects.core.datasource)

View file

@ -5,7 +5,8 @@ import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.blockchainsdk.signer.TransactionSignerFactory import com.tangem.blockchainsdk.signer.TransactionSignerFactory
import com.tangem.common.routing.AppRouter import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.filter.OneTimeEventFilter import com.tangem.core.analytics.filter.OneTimeEventFilter
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.core.navigation.share.ShareManager import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.navigation.url.UrlOpener
import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.datasource.connection.NetworkConnectionManager
@ -34,6 +35,7 @@ import com.tangem.tap.common.log.TangemAppLoggerInitializer
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
import com.tangem.tap.features.home.featuretoggles.HomeFeatureToggles import com.tangem.tap.features.home.featuretoggles.HomeFeatureToggles
import com.tangem.tap.proxy.AppStateHolder import com.tangem.tap.proxy.AppStateHolder
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.EntryPoint import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
@ -52,6 +54,8 @@ interface ApplicationEntryPoint {
fun getFeatureTogglesManager(): FeatureTogglesManager fun getFeatureTogglesManager(): FeatureTogglesManager
fun getExcludedBlockchainsManager(): ExcludedBlockchainsManager
fun getNetworkConnectionManager(): NetworkConnectionManager fun getNetworkConnectionManager(): NetworkConnectionManager
fun getCardScanningFeatureToggles(): CardScanningFeatureToggles fun getCardScanningFeatureToggles(): CardScanningFeatureToggles
@ -113,4 +117,6 @@ interface ApplicationEntryPoint {
fun getOnrampFeatureToggles(): OnrampFeatureToggles fun getOnrampFeatureToggles(): OnrampFeatureToggles
fun getOnboardingV2FeatureToggles(): OnboardingV2FeatureToggles fun getOnboardingV2FeatureToggles(): OnboardingV2FeatureToggles
fun getCoroutineDispatcherProvider(): CoroutineDispatcherProvider
} }

View file

@ -17,7 +17,8 @@ import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic import com.tangem.core.analytics.models.Basic
import com.tangem.core.analytics.models.Basic.TransactionSent.WalletForm import com.tangem.core.analytics.models.Basic.TransactionSent.WalletForm
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.datasource.api.common.MoshiConverter import com.tangem.datasource.api.common.MoshiConverter
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.datasource.connection.NetworkConnectionManager
@ -57,9 +58,11 @@ import com.tangem.tap.domain.tasks.product.DerivationsFinder
import com.tangem.tap.features.home.featuretoggles.HomeFeatureToggles import com.tangem.tap.features.home.featuretoggles.HomeFeatureToggles
import com.tangem.tap.proxy.AppStateHolder import com.tangem.tap.proxy.AppStateHolder
import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.wallet.BuildConfig import com.tangem.wallet.BuildConfig
import dagger.hilt.EntryPoints import dagger.hilt.EntryPoints
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.rekotlin.Store import org.rekotlin.Store
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository as WalletConnect2Repository import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository as WalletConnect2Repository
@ -88,6 +91,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
private val featureTogglesManager: FeatureTogglesManager private val featureTogglesManager: FeatureTogglesManager
get() = entryPoint.getFeatureTogglesManager() get() = entryPoint.getFeatureTogglesManager()
private val excludedBlockchainsManager: ExcludedBlockchainsManager
get() = entryPoint.getExcludedBlockchainsManager()
private val networkConnectionManager: NetworkConnectionManager private val networkConnectionManager: NetworkConnectionManager
get() = entryPoint.getNetworkConnectionManager() get() = entryPoint.getNetworkConnectionManager()
@ -180,6 +186,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles
get() = entryPoint.getOnboardingV2FeatureToggles() get() = entryPoint.getOnboardingV2FeatureToggles()
private val dispatchers: CoroutineDispatcherProvider
get() = entryPoint.getCoroutineDispatcherProvider()
// endregion // endregion
override fun onCreate() { override fun onCreate() {
@ -200,9 +209,11 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
// TODO: Try to performance and user experience. // TODO: Try to performance and user experience.
// [REDACTED_JIRA] // [REDACTED_JIRA]
runBlocking { runBlocking {
featureTogglesManager.init() awaitAll(
async { featureTogglesManager.init() },
initWithConfigDependency(environmentConfig = environmentConfigStorage.initialize()) async { excludedBlockchainsManager.init() },
async { initWithConfigDependency(environmentConfig = environmentConfigStorage.initialize()) },
)
} }
loadNativeLibraries() loadNativeLibraries()
@ -216,7 +227,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
derivationsFinder = DerivationsFinder( derivationsFinder = DerivationsFinder(
appPreferencesStore = appPreferencesStore, appPreferencesStore = appPreferencesStore,
dispatchers = AppCoroutineDispatcherProvider(), dispatchers = dispatchers,
) )
appStateHolder.mainStore = store appStateHolder.mainStore = store

View file

@ -1,18 +0,0 @@
package com.tangem.tap.common.extensions
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.extensions.remove
fun Blockchain.getNetworkStandardName(): String {
return when (this) {
Blockchain.Ethereum, Blockchain.EthereumTestnet -> "ERC20"
Blockchain.BSC, Blockchain.BSCTestnet -> "BEP20"
Blockchain.Binance, Blockchain.BinanceTestnet -> "BEP2"
Blockchain.Tron, Blockchain.TronTestnet -> "TRC20"
Blockchain.TON -> "TON"
else -> ""
}
}
val Blockchain.networkNameWithoutTestnet
get() = this.getNetworkName().remove(" Testnet")

View file

@ -1,6 +1,6 @@
package com.tangem.tap.di.domain package com.tangem.tap.di.domain
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase

View file

@ -1,6 +1,6 @@
package com.tangem.tap.domain.scanCard package com.tangem.tap.domain.scanCard
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
/** /**
* Add custom token feature toggles * Add custom token feature toggles

View file

@ -3,7 +3,7 @@ package com.tangem.tap.domain.walletconnect2.di
import android.app.Application import android.app.Application
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.datasource.di.SdkMoshi import com.tangem.datasource.di.SdkMoshi
import com.tangem.datasource.files.FileReader import com.tangem.datasource.files.FileReader
import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrenciesRepository

View file

@ -1,6 +1,6 @@
package com.tangem.tap.domain.walletconnect2.toggles package com.tangem.tap.domain.walletconnect2.toggles
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
internal class WalletConnectFeatureToggles( internal class WalletConnectFeatureToggles(
private val featureTogglesManager: FeatureTogglesManager, private val featureTogglesManager: FeatureTogglesManager,

View file

@ -1,6 +1,6 @@
package com.tangem.tap.features.home.featuretoggles package com.tangem.tap.features.home.featuretoggles
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import javax.inject.Inject import javax.inject.Inject
class HomeFeatureToggles @Inject constructor( class HomeFeatureToggles @Inject constructor(

View file

@ -7,7 +7,7 @@ plugins {
} }
android { android {
namespace = "com.tangem.core.featuretoggles" namespace = "com.tangem.core.configtoggle"
} }
dependencies { dependencies {

View file

@ -0,0 +1,10 @@
[
{
"name": "",
"version": "undefined"
},
{
"name": "NEXA",
"version": "undefined"
}
]

View file

@ -0,0 +1,8 @@
package com.tangem.core.configtoggle.blockchain
interface ExcludedBlockchainsManager {
val excludedBlockchainsIds: Set<String>
suspend fun init()
}

View file

@ -0,0 +1,10 @@
package com.tangem.core.configtoggle.blockchain
interface MutableExcludedBlockchainsManager : ExcludedBlockchainsManager {
suspend fun excludeBlockchain(mainnetId: String, isExcluded: Boolean)
fun isMatchLocalConfig(): Boolean
suspend fun recoverLocalConfig()
}

View file

@ -0,0 +1,74 @@
package com.tangem.core.configtoggle.blockchain.impl
import com.tangem.core.configtoggle.blockchain.MutableExcludedBlockchainsManager
import com.tangem.core.configtoggle.storage.TogglesStorage
import com.tangem.core.configtoggle.utils.associateToggles
import com.tangem.core.configtoggle.version.VersionProvider
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
import com.tangem.datasource.local.preferences.utils.store
internal class DefaultExcludedBlockchainsManager(
private val localTogglesStorage: TogglesStorage,
private val appPreferencesStore: AppPreferencesStore,
private val versionProvider: VersionProvider,
) : MutableExcludedBlockchainsManager {
private var isInitialized: Boolean = false
private lateinit var currentExcludedBlockchainsIds: Set<String>
private lateinit var localExcludedBlockchainsIds: Set<String>
override val excludedBlockchainsIds: Set<String>
get() {
if (!isInitialized) error("ExcludedBlockchainsManager is not initialized")
return currentExcludedBlockchainsIds
}
override suspend fun init() {
localTogglesStorage.populate(path = "configs/excluded_blockchains_config")
val storedExcludedBlockchainsIds = appPreferencesStore.getSyncOrDefault(
key = PreferencesKeys.EXCLUDED_BLOCKCHAINS_KEY,
default = emptySet(),
)
localExcludedBlockchainsIds = localTogglesStorage.toggles
.associateToggles(currentVersion = versionProvider.get().orEmpty())
.filterValues { isIncluded -> !isIncluded }
.keys
currentExcludedBlockchainsIds = localExcludedBlockchainsIds + storedExcludedBlockchainsIds
isInitialized = true
}
override suspend fun excludeBlockchain(mainnetId: String, isExcluded: Boolean) {
if (isExcluded) {
currentExcludedBlockchainsIds += mainnetId
} else {
currentExcludedBlockchainsIds -= mainnetId
}
storeCurrent()
}
override fun isMatchLocalConfig(): Boolean {
return currentExcludedBlockchainsIds == localExcludedBlockchainsIds
}
override suspend fun recoverLocalConfig() {
currentExcludedBlockchainsIds = localExcludedBlockchainsIds
storeCurrent()
}
private suspend fun storeCurrent() {
appPreferencesStore.store(
PreferencesKeys.EXCLUDED_BLOCKCHAINS_KEY,
currentExcludedBlockchainsIds,
)
}
}

View file

@ -0,0 +1,49 @@
package com.tangem.core.configtoggle.di
import android.content.Context
import com.tangem.core.configtoggle.BuildConfig
import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
import com.tangem.core.configtoggle.blockchain.MutableExcludedBlockchainsManager
import com.tangem.core.configtoggle.blockchain.impl.DefaultExcludedBlockchainsManager
import com.tangem.core.configtoggle.storage.LocalTogglesStorage
import com.tangem.core.configtoggle.version.DefaultVersionProvider
import com.tangem.datasource.asset.loader.AssetLoader
import com.tangem.datasource.local.preferences.AppPreferencesStore
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object ExcludedBlockchainsManagerModule {
@Provides
@Singleton
fun provideExcludedBlockchainsManager(
@ApplicationContext context: Context,
assetLoader: AssetLoader,
appPreferencesStore: AppPreferencesStore,
): ExcludedBlockchainsManager {
val localTogglesStorage = LocalTogglesStorage(assetLoader)
val versionProvider = DefaultVersionProvider(context)
return DefaultExcludedBlockchainsManager(
localTogglesStorage,
appPreferencesStore,
versionProvider,
)
}
@Provides
@Singleton
fun provideMutableExcludedBlockchainsManager(
manager: ExcludedBlockchainsManager,
): MutableExcludedBlockchainsManager? {
if (!BuildConfig.TESTER_MENU_ENABLED) return null
return manager as MutableExcludedBlockchainsManager
}
}

View file

@ -1,12 +1,12 @@
package com.tangem.core.featuretoggle.di package com.tangem.core.configtoggle.di
import android.content.Context import android.content.Context
import com.tangem.core.featuretoggle.manager.DevFeatureTogglesManager import com.tangem.core.configtoggle.BuildConfig
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.core.featuretoggle.manager.ProdFeatureTogglesManager import com.tangem.core.configtoggle.feature.impl.DevFeatureTogglesManager
import com.tangem.core.featuretoggle.storage.LocalFeatureTogglesStorage import com.tangem.core.configtoggle.feature.impl.ProdFeatureTogglesManager
import com.tangem.core.featuretoggle.version.DefaultVersionProvider import com.tangem.core.configtoggle.storage.LocalTogglesStorage
import com.tangem.core.featuretoggles.BuildConfig import com.tangem.core.configtoggle.version.DefaultVersionProvider
import com.tangem.datasource.asset.loader.AssetLoader import com.tangem.datasource.asset.loader.AssetLoader
import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.AppPreferencesStore
import dagger.Module import dagger.Module
@ -27,18 +27,18 @@ internal object FeatureTogglesManagerModule {
assetLoader: AssetLoader, assetLoader: AssetLoader,
appPreferencesStore: AppPreferencesStore, appPreferencesStore: AppPreferencesStore,
): FeatureTogglesManager { ): FeatureTogglesManager {
val localFeatureTogglesStorage = LocalFeatureTogglesStorage(assetLoader) val localTogglesStorage = LocalTogglesStorage(assetLoader)
val versionProvider = DefaultVersionProvider(context) val versionProvider = DefaultVersionProvider(context)
return if (BuildConfig.TESTER_MENU_ENABLED) { return if (BuildConfig.TESTER_MENU_ENABLED) {
DevFeatureTogglesManager( DevFeatureTogglesManager(
localFeatureTogglesStorage = localFeatureTogglesStorage, localTogglesStorage = localTogglesStorage,
appPreferencesStore = appPreferencesStore, appPreferencesStore = appPreferencesStore,
versionProvider = versionProvider, versionProvider = versionProvider,
) )
} else { } else {
ProdFeatureTogglesManager( ProdFeatureTogglesManager(
localFeatureTogglesStorage = localFeatureTogglesStorage, localTogglesStorage = localTogglesStorage,
versionProvider = versionProvider, versionProvider = versionProvider,
) )
} }

View file

@ -1,4 +1,4 @@
package com.tangem.core.featuretoggle.manager package com.tangem.core.configtoggle.feature
/** /**
* Component for getting information about the availability of feature toggles * Component for getting information about the availability of feature toggles

View file

@ -1,4 +1,4 @@
package com.tangem.core.featuretoggle.manager package com.tangem.core.configtoggle.feature
/** /**
* Component for change information about the availability of feature toggles * Component for change information about the availability of feature toggles

View file

@ -1,9 +1,10 @@
package com.tangem.core.featuretoggle.manager package com.tangem.core.configtoggle.feature.impl
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import com.tangem.core.featuretoggle.storage.FeatureTogglesStorage import com.tangem.core.configtoggle.feature.MutableFeatureTogglesManager
import com.tangem.core.featuretoggle.utils.associateToggles import com.tangem.core.configtoggle.storage.TogglesStorage
import com.tangem.core.featuretoggle.version.VersionProvider import com.tangem.core.configtoggle.utils.associateToggles
import com.tangem.core.configtoggle.version.VersionProvider
import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
@ -13,12 +14,12 @@ import kotlin.properties.Delegates
/** /**
* Feature toggles manager implementation in DEV build * Feature toggles manager implementation in DEV build
* *
* @property localFeatureTogglesStorage local feature toggles storage * @property localTogglesStorage local feature toggles storage
* @property appPreferencesStore application local store * @property appPreferencesStore application local store
* @property versionProvider application version provider * @property versionProvider application version provider
*/ */
internal class DevFeatureTogglesManager( internal class DevFeatureTogglesManager(
private val localFeatureTogglesStorage: FeatureTogglesStorage, private val localTogglesStorage: TogglesStorage,
private val appPreferencesStore: AppPreferencesStore, private val appPreferencesStore: AppPreferencesStore,
private val versionProvider: VersionProvider, private val versionProvider: VersionProvider,
) : MutableFeatureTogglesManager { ) : MutableFeatureTogglesManager {
@ -27,13 +28,13 @@ internal class DevFeatureTogglesManager(
private var localFeatureTogglesMap: Map<String, Boolean> by Delegates.notNull() private var localFeatureTogglesMap: Map<String, Boolean> by Delegates.notNull()
override suspend fun init() { override suspend fun init() {
localFeatureTogglesStorage.init() localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
val savedFeatureToggles = appPreferencesStore.getObjectSyncOrNull<Map<String, Boolean>>( val savedFeatureToggles = appPreferencesStore.getObjectSyncOrNull<Map<String, Boolean>>(
key = PreferencesKeys.FEATURE_TOGGLES_KEY, key = PreferencesKeys.FEATURE_TOGGLES_KEY,
) ?: emptyMap() ) ?: emptyMap()
val localFeatureToggles = localFeatureTogglesStorage.featureToggles val localFeatureToggles = localTogglesStorage.toggles
.associateToggles(currentVersion = versionProvider.get().orEmpty()) .associateToggles(currentVersion = versionProvider.get().orEmpty())
localFeatureTogglesMap = localFeatureToggles localFeatureTogglesMap = localFeatureToggles

View file

@ -0,0 +1,6 @@
package com.tangem.core.configtoggle.feature.impl
internal object FeatureTogglesConstants {
const val LOCAL_CONFIG_PATH: String = "configs/feature_toggles_config"
}

View file

@ -1,27 +1,28 @@
package com.tangem.core.featuretoggle.manager package com.tangem.core.configtoggle.feature.impl
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import com.tangem.core.featuretoggle.storage.FeatureTogglesStorage import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.core.featuretoggle.utils.associateToggles import com.tangem.core.configtoggle.storage.TogglesStorage
import com.tangem.core.featuretoggle.version.VersionProvider import com.tangem.core.configtoggle.utils.associateToggles
import com.tangem.core.configtoggle.version.VersionProvider
import kotlin.properties.Delegates import kotlin.properties.Delegates
/** /**
* Feature toggles manager implementation in PROD build * Feature toggles manager implementation in PROD build
* *
* @property localFeatureTogglesStorage local feature toggles storage * @property localTogglesStorage local feature toggles storage
* @property versionProvider application version provider * @property versionProvider application version provider
*/ */
internal class ProdFeatureTogglesManager( internal class ProdFeatureTogglesManager(
private val localFeatureTogglesStorage: FeatureTogglesStorage, private val localTogglesStorage: TogglesStorage,
private val versionProvider: VersionProvider, private val versionProvider: VersionProvider,
) : FeatureTogglesManager { ) : FeatureTogglesManager {
private var featureToggles: Map<String, Boolean> by Delegates.notNull() private var featureToggles: Map<String, Boolean> by Delegates.notNull()
override suspend fun init() { override suspend fun init() {
localFeatureTogglesStorage.init() localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
featureToggles = localFeatureTogglesStorage.featureToggles featureToggles = localTogglesStorage.toggles
.associateToggles(currentVersion = versionProvider.get() ?: "") .associateToggles(currentVersion = versionProvider.get() ?: "")
} }

View file

@ -0,0 +1,20 @@
package com.tangem.core.configtoggle.storage
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* Data model with information about config toggle
*
* @property name toggle name
* @property version version in which the toggle will be enabled
*
* IMPORTANT: if the version is "undefined", it means that toggle is disabled!
*
[REDACTED_AUTHOR]
*/
@JsonClass(generateAdapter = true)
internal data class ConfigToggle(
@Json(name = "name") val name: String,
@Json(name = "version") val version: String,
)

View file

@ -0,0 +1,24 @@
package com.tangem.core.configtoggle.storage
import com.tangem.datasource.asset.loader.AssetLoader
import kotlin.properties.Delegates
/**
* Storage implementation for storing local feature toggles.
* Feature toggles are declared in file [LOCAL_CONFIG_PATH].
*
* @property assetLoader asset loader
*
[REDACTED_AUTHOR]
*/
internal class LocalTogglesStorage(
private val assetLoader: AssetLoader,
) : TogglesStorage {
override var toggles: List<ConfigToggle> by Delegates.notNull()
private set
override suspend fun populate(path: String) {
toggles = assetLoader.loadList<ConfigToggle>(path)
}
}

View file

@ -0,0 +1,15 @@
package com.tangem.core.configtoggle.storage
/**
* Component that initializes and stores a list of feature toggles
*
[REDACTED_AUTHOR]
*/
internal interface TogglesStorage {
/** List of feature toggles */
val toggles: List<ConfigToggle>
/** Populate the storage with toggles */
suspend fun populate(path: String)
}

View file

@ -0,0 +1,13 @@
package com.tangem.core.configtoggle.utils
import com.tangem.core.configtoggle.storage.ConfigToggle
import com.tangem.core.configtoggle.version.VersionAvailabilityContract
internal fun List<ConfigToggle>.associateToggles(currentVersion: String): Map<String, Boolean> {
return associate { localToggle ->
Pair(
first = localToggle.name,
second = VersionAvailabilityContract(currentVersion, localToggle.version),
)
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.core.featuretoggle.version package com.tangem.core.configtoggle.version
import android.content.Context import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager

View file

@ -1,4 +1,4 @@
package com.tangem.core.featuretoggle.contract package com.tangem.core.configtoggle.version
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import timber.log.Timber import timber.log.Timber

View file

@ -1,4 +1,4 @@
package com.tangem.core.featuretoggle.contract package com.tangem.core.configtoggle.version
/** /**
* Version contract to evaluate availability of feature toggle * Version contract to evaluate availability of feature toggle

View file

@ -1,4 +1,4 @@
package com.tangem.core.featuretoggle.version package com.tangem.core.configtoggle.version
/** Application version provider */ /** Application version provider */
internal interface VersionProvider { internal interface VersionProvider {

View file

@ -1,6 +1,7 @@
package com.tangem.core.featuretoggle.contract package com.tangem.core.configtoggle.contract
import com.google.common.truth.Truth import com.google.common.truth.Truth
import com.tangem.core.configtoggle.version.VersionAvailabilityContract
import org.junit.Test import org.junit.Test
/** /**

View file

@ -1,6 +1,7 @@
package com.tangem.core.featuretoggle.contract package com.tangem.core.configtoggle.contract
import com.google.common.truth.Truth import com.google.common.truth.Truth
import com.tangem.core.configtoggle.version.Version
import org.junit.Test import org.junit.Test
/** /**

View file

@ -1,11 +1,13 @@
package com.tangem.core.featuretoggle.manager package com.tangem.core.configtoggle.manager
import android.annotation.SuppressLint import android.annotation.SuppressLint
import com.google.common.truth.Truth import com.google.common.truth.Truth
import com.tangem.core.featuretoggle.storage.FeatureToggle import com.tangem.core.configtoggle.feature.impl.DevFeatureTogglesManager
import com.tangem.core.featuretoggle.storage.FeatureTogglesStorage import com.tangem.core.configtoggle.feature.impl.FeatureTogglesConstants
import com.tangem.core.featuretoggle.utils.associateToggles import com.tangem.core.configtoggle.storage.ConfigToggle
import com.tangem.core.featuretoggle.version.VersionProvider import com.tangem.core.configtoggle.storage.TogglesStorage
import com.tangem.core.configtoggle.utils.associateToggles
import com.tangem.core.configtoggle.version.VersionProvider
import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
@ -19,13 +21,13 @@ import kotlin.collections.set
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
internal class DevFeatureTogglesManagerTest { internal class DevTogglesManagerTest {
private val localFeatureTogglesStorage = mockk<FeatureTogglesStorage>() private val localTogglesStorage = mockk<TogglesStorage>()
private val appPreferenceStore = mockk<AppPreferencesStore>(relaxed = true) private val appPreferenceStore = mockk<AppPreferencesStore>(relaxed = true)
private val versionProvider = mockk<VersionProvider>() private val versionProvider = mockk<VersionProvider>()
private val manager = DevFeatureTogglesManager( private val manager = DevFeatureTogglesManager(
localFeatureTogglesStorage = localFeatureTogglesStorage, localTogglesStorage = localTogglesStorage,
appPreferencesStore = appPreferenceStore, appPreferencesStore = appPreferenceStore,
versionProvider = versionProvider, versionProvider = versionProvider,
) )
@ -34,17 +36,17 @@ internal class DevFeatureTogglesManagerTest {
fun `successfully initialize storage if shared prefs kept feature toggles`() = runTest { fun `successfully initialize storage if shared prefs kept feature toggles`() = runTest {
val currentVersion = "0.1.0" val currentVersion = "0.1.0"
coEvery { localFeatureTogglesStorage.init() } just Runs coEvery { localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH) } just Runs
coEvery { coEvery {
appPreferenceStore.getObjectSyncOrNull<Map<String, Boolean>>(PreferencesKeys.FEATURE_TOGGLES_KEY) appPreferenceStore.getObjectSyncOrNull<Map<String, Boolean>>(PreferencesKeys.FEATURE_TOGGLES_KEY)
} returns savedFeatureTogglesMap } returns savedFeatureTogglesMap
coEvery { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles coEvery { localTogglesStorage.toggles } returns localFeatureToggles
coEvery { versionProvider.get() } returns currentVersion coEvery { versionProvider.get() } returns currentVersion
manager.init() manager.init()
coVerifyOrder { coVerifyOrder {
localFeatureTogglesStorage.init() localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
versionProvider.get() versionProvider.get()
} }
@ -61,17 +63,17 @@ internal class DevFeatureTogglesManagerTest {
fun `successfully initialize storage if shared prefs kept empty list`() = runTest { fun `successfully initialize storage if shared prefs kept empty list`() = runTest {
val currentVersion = "0.1.0" val currentVersion = "0.1.0"
coEvery { localFeatureTogglesStorage.init() } just Runs coEvery { localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH) } just Runs
coEvery { coEvery {
appPreferenceStore.getObjectSyncOrNull<Map<String, Boolean>>(PreferencesKeys.FEATURE_TOGGLES_KEY) appPreferenceStore.getObjectSyncOrNull<Map<String, Boolean>>(PreferencesKeys.FEATURE_TOGGLES_KEY)
} returns emptyMap() } returns emptyMap()
coEvery { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles coEvery { localTogglesStorage.toggles } returns localFeatureToggles
coEvery { versionProvider.get() } returns currentVersion coEvery { versionProvider.get() } returns currentVersion
manager.init() manager.init()
coVerifyOrder { coVerifyOrder {
localFeatureTogglesStorage.init() localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
versionProvider.get() versionProvider.get()
} }
@ -88,17 +90,17 @@ internal class DevFeatureTogglesManagerTest {
fun `successfully initialize storage if shared prefs didn't keep feature toggles`() = runTest { fun `successfully initialize storage if shared prefs didn't keep feature toggles`() = runTest {
val currentVersion = "0.1.0" val currentVersion = "0.1.0"
coEvery { localFeatureTogglesStorage.init() } just Runs coEvery { localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH) } just Runs
coEvery { coEvery {
appPreferenceStore.getObjectSyncOrNull<Map<String, Boolean>>(PreferencesKeys.FEATURE_TOGGLES_KEY) appPreferenceStore.getObjectSyncOrNull<Map<String, Boolean>>(PreferencesKeys.FEATURE_TOGGLES_KEY)
} returns null } returns null
coEvery { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles coEvery { localTogglesStorage.toggles } returns localFeatureToggles
coEvery { versionProvider.get() } returns currentVersion coEvery { versionProvider.get() } returns currentVersion
manager.init() manager.init()
coVerifyOrder { coVerifyOrder {
localFeatureTogglesStorage.init() localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
versionProvider.get() versionProvider.get()
} }
@ -111,15 +113,15 @@ internal class DevFeatureTogglesManagerTest {
@Test @Test
fun `successfully initialize storage if versionProvider returns null`() = runTest { fun `successfully initialize storage if versionProvider returns null`() = runTest {
coEvery { localFeatureTogglesStorage.init() } just Runs coEvery { localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH) } just Runs
coEvery { appPreferenceStore.getSyncOrNull(PreferencesKeys.FEATURE_TOGGLES_KEY) } returns savedFeatureToggles coEvery { appPreferenceStore.getSyncOrNull(PreferencesKeys.FEATURE_TOGGLES_KEY) } returns savedFeatureToggles
coEvery { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles coEvery { localTogglesStorage.toggles } returns localFeatureToggles
coEvery { versionProvider.get() } returns null coEvery { versionProvider.get() } returns null
manager.init() manager.init()
coVerifyOrder { coVerifyOrder {
localFeatureTogglesStorage.init() localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
versionProvider.get() versionProvider.get()
} }
@ -222,8 +224,8 @@ internal class DevFeatureTogglesManagerTest {
) )
val localFeatureToggles = listOf( val localFeatureToggles = listOf(
FeatureToggle(name = "INACTIVE_TEST_FEATURE_ENABLED", version = "undefined"), ConfigToggle(name = "INACTIVE_TEST_FEATURE_ENABLED", version = "undefined"),
FeatureToggle(name = "ACTIVE2_TEST_FEATURE_ENABLED", version = "1.0.0"), ConfigToggle(name = "ACTIVE2_TEST_FEATURE_ENABLED", version = "1.0.0"),
) )
} }
} }

View file

@ -1,11 +1,13 @@
package com.tangem.core.featuretoggle.manager package com.tangem.core.configtoggle.manager
import android.content.pm.PackageManager import android.content.pm.PackageManager
import com.google.common.truth.Truth import com.google.common.truth.Truth
import com.tangem.core.featuretoggle.storage.FeatureToggle import com.tangem.core.configtoggle.feature.impl.FeatureTogglesConstants
import com.tangem.core.featuretoggle.storage.FeatureTogglesStorage import com.tangem.core.configtoggle.feature.impl.ProdFeatureTogglesManager
import com.tangem.core.featuretoggle.utils.associateToggles import com.tangem.core.configtoggle.storage.ConfigToggle
import com.tangem.core.featuretoggle.version.VersionProvider import com.tangem.core.configtoggle.storage.TogglesStorage
import com.tangem.core.configtoggle.utils.associateToggles
import com.tangem.core.configtoggle.version.VersionProvider
import io.mockk.* import io.mockk.*
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
@ -13,24 +15,24 @@ import org.junit.Test
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
internal class ProdFeatureTogglesManagerTest { internal class ProdTogglesManagerTest {
private val localFeatureTogglesStorage = mockk<FeatureTogglesStorage>() private val localTogglesStorage = mockk<TogglesStorage>()
private val versionProvider = mockk<VersionProvider>() private val versionProvider = mockk<VersionProvider>()
private val manager = ProdFeatureTogglesManager(localFeatureTogglesStorage, versionProvider) private val manager = ProdFeatureTogglesManager(localTogglesStorage, versionProvider)
@Test @Test
fun `successfully initialize storage`() = runTest { fun `successfully initialize storage`() = runTest {
val currentVersion = "1.0.0" val currentVersion = "1.0.0"
coEvery { localFeatureTogglesStorage.init() } just Runs coEvery { localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH) } just Runs
every { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles every { localTogglesStorage.toggles } returns localFeatureToggles
every { versionProvider.get() } returns currentVersion every { versionProvider.get() } returns currentVersion
manager.init() manager.init()
coVerifyOrder { coVerifyOrder {
localFeatureTogglesStorage.init() localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
versionProvider.get() versionProvider.get()
} }
@ -40,14 +42,14 @@ internal class ProdFeatureTogglesManagerTest {
@Test @Test
fun `successfully initialize storage if versionProvider returns null`() = runTest { fun `successfully initialize storage if versionProvider returns null`() = runTest {
coEvery { localFeatureTogglesStorage.init() } just Runs coEvery { localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH) } just Runs
every { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles every { localTogglesStorage.toggles } returns localFeatureToggles
every { versionProvider.get() } returns null every { versionProvider.get() } returns null
manager.init() manager.init()
coVerifyOrder { coVerifyOrder {
localFeatureTogglesStorage.init() localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
versionProvider.get() versionProvider.get()
} }
@ -56,8 +58,8 @@ internal class ProdFeatureTogglesManagerTest {
@Test @Test
fun `failure initialize storage if localFeatureTogglesStorage throws exception`() = runTest { fun `failure initialize storage if localFeatureTogglesStorage throws exception`() = runTest {
coEvery { localFeatureTogglesStorage.init() } just Runs coEvery { localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH) } just Runs
every { localFeatureTogglesStorage.featureToggles } throws IllegalStateException( every { localTogglesStorage.toggles } throws IllegalStateException(
"Property featureToggles should be initialized before get.", "Property featureToggles should be initialized before get.",
) )
@ -72,14 +74,14 @@ internal class ProdFeatureTogglesManagerTest {
Truth.assertThat(it).isInstanceOf(IllegalStateException::class.java) Truth.assertThat(it).isInstanceOf(IllegalStateException::class.java)
} }
coVerifyOrder { localFeatureTogglesStorage.init() } coVerifyOrder { localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH) }
verifyAll(inverse = true) { versionProvider.get() } verifyAll(inverse = true) { versionProvider.get() }
} }
@Test @Test
fun `failure initialize storage if versionProvider throws exception`() = runTest { fun `failure initialize storage if versionProvider throws exception`() = runTest {
coEvery { localFeatureTogglesStorage.init() } just Runs coEvery { localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH) } just Runs
every { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles every { localTogglesStorage.toggles } returns localFeatureToggles
every { versionProvider.get() } throws PackageManager.NameNotFoundException() every { versionProvider.get() } throws PackageManager.NameNotFoundException()
runCatching { manager.init() } runCatching { manager.init() }
@ -89,7 +91,7 @@ internal class ProdFeatureTogglesManagerTest {
} }
coVerifyOrder { coVerifyOrder {
localFeatureTogglesStorage.init() localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
versionProvider.get() versionProvider.get()
} }
} }
@ -122,8 +124,8 @@ internal class ProdFeatureTogglesManagerTest {
private companion object { private companion object {
val localFeatureToggles = listOf( val localFeatureToggles = listOf(
FeatureToggle(name = "INACTIVE_TEST_FEATURE_ENABLED", version = "undefined"), ConfigToggle(name = "INACTIVE_TEST_FEATURE_ENABLED", version = "undefined"),
FeatureToggle(name = "ACTIVE2_TEST_FEATURE_ENABLED", version = "1.0.0"), ConfigToggle(name = "ACTIVE2_TEST_FEATURE_ENABLED", version = "1.0.0"),
) )
val disabledFeatureToggles = mapOf( val disabledFeatureToggles = mapOf(

View file

@ -1,10 +1,11 @@
package com.tangem.core.featuretoggle.storage package com.tangem.core.configtoggle.storage
import android.annotation.SuppressLint import android.annotation.SuppressLint
import com.google.common.truth.Truth import com.google.common.truth.Truth
import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import com.squareup.moshi.Types import com.squareup.moshi.Types
import com.tangem.core.configtoggle.feature.impl.FeatureTogglesConstants
import com.tangem.datasource.asset.loader.AssetLoader import com.tangem.datasource.asset.loader.AssetLoader
import com.tangem.datasource.asset.reader.AssetReader import com.tangem.datasource.asset.reader.AssetReader
import io.mockk.* import io.mockk.*
@ -16,16 +17,16 @@ import java.io.IOException
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
internal class LocalFeatureTogglesStorageTest { internal class LocalTogglesStorageTest {
private val assetReader = mockk<AssetReader>() private val assetReader = mockk<AssetReader>()
private val moshi = mockk<Moshi>() private val moshi = mockk<Moshi>()
private val jsonAdapter = mockk<JsonAdapter<List<FeatureToggle>>>() private val jsonAdapter = mockk<JsonAdapter<List<ConfigToggle>>>()
// Impossible to mockk AssetLoader because it implement inline functions // Impossible to mockk AssetLoader because it implement inline functions
private val assetLoader = AssetLoader(assetReader = assetReader, moshi = moshi) private val assetLoader = AssetLoader(assetReader = assetReader, moshi = moshi)
private val storage = LocalFeatureTogglesStorage(assetLoader) private val storage = LocalTogglesStorage(assetLoader)
@Test @Test
fun `successfully initialize storage`() = runTest { fun `successfully initialize storage`() = runTest {
@ -33,14 +34,14 @@ internal class LocalFeatureTogglesStorageTest {
everyCreatingMoshiAdapter() returns jsonAdapter everyCreatingMoshiAdapter() returns jsonAdapter
everyMappingJson() returns featureToggles everyMappingJson() returns featureToggles
storage.init() storage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
coVerifyOrder { coVerifyOrder {
assetReader.read(CONFIG_FILE_NAME) assetReader.read(CONFIG_FILE_NAME)
jsonAdapter.fromJson(json) jsonAdapter.fromJson(json)
} }
Truth.assertThat(storage.featureToggles).containsExactlyElementsIn(featureToggles) Truth.assertThat(storage.toggles).containsExactlyElementsIn(featureToggles)
} }
@Test @Test
@ -49,33 +50,33 @@ internal class LocalFeatureTogglesStorageTest {
everyCreatingMoshiAdapter() returns jsonAdapter everyCreatingMoshiAdapter() returns jsonAdapter
everyMappingJson() throws IOException() everyMappingJson() throws IOException()
storage.init() storage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
coVerifyOrder { coVerifyOrder {
assetReader.read(CONFIG_FILE_NAME) assetReader.read(CONFIG_FILE_NAME)
jsonAdapter.fromJson(json) jsonAdapter.fromJson(json)
} }
Truth.assertThat(storage.featureToggles).containsExactlyElementsIn(emptyList<FeatureToggle>()) Truth.assertThat(storage.toggles).containsExactlyElementsIn(emptyList<ConfigToggle>())
} }
@Test @Test
fun `failure initialize storage if jsonAdapter throws exception`() = runTest { fun `failure initialize storage if jsonAdapter throws exception`() = runTest {
everyReadingJson() throws IOException() everyReadingJson() throws IOException()
storage.init() storage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
coVerifyOrder { assetReader.read(CONFIG_FILE_NAME) } coVerifyOrder { assetReader.read(CONFIG_FILE_NAME) }
verifyAll(inverse = true) { jsonAdapter.fromJson(any<String>()) } verifyAll(inverse = true) { jsonAdapter.fromJson(any<String>()) }
Truth.assertThat(storage.featureToggles).containsExactlyElementsIn(emptyList<FeatureToggle>()) Truth.assertThat(storage.toggles).containsExactlyElementsIn(emptyList<ConfigToggle>())
} }
private fun everyReadingJson() = coEvery { assetReader.read(CONFIG_FILE_NAME) } private fun everyReadingJson() = coEvery { assetReader.read(CONFIG_FILE_NAME) }
private fun everyCreatingMoshiAdapter() = every { private fun everyCreatingMoshiAdapter() = every {
val types = Types.newParameterizedType(List::class.java, FeatureToggle::class.java) val types = Types.newParameterizedType(List::class.java, ConfigToggle::class.java)
moshi.adapter<List<FeatureToggle>>(types) moshi.adapter<List<ConfigToggle>>(types)
} }
private fun everyMappingJson() = every { jsonAdapter.fromJson(json) } private fun everyMappingJson() = every { jsonAdapter.fromJson(json) }
@ -97,8 +98,8 @@ internal class LocalFeatureTogglesStorageTest {
""".trimIndent() """.trimIndent()
val featureToggles = listOf( val featureToggles = listOf(
FeatureToggle(name = "INACTIVE_TEST_FEATURE_ENABLED", version = "undefined"), ConfigToggle(name = "INACTIVE_TEST_FEATURE_ENABLED", version = "undefined"),
FeatureToggle(name = "ACTIVE2_TEST_FEATURE_ENABLED", version = "1.0.0"), ConfigToggle(name = "ACTIVE2_TEST_FEATURE_ENABLED", version = "1.0.0"),
) )
} }
} }

View file

@ -55,6 +55,8 @@ object PreferencesKeys {
val FEATURE_TOGGLES_KEY by lazy { stringPreferencesKey(name = "featureToggles") } val FEATURE_TOGGLES_KEY by lazy { stringPreferencesKey(name = "featureToggles") }
val EXCLUDED_BLOCKCHAINS_KEY by lazy { stringSetPreferencesKey(name = "excludedBlockchains") }
val WAS_TWINS_ONBOARDING_SHOWN by lazy { booleanPreferencesKey(name = "twinsOnboardingShown") } val WAS_TWINS_ONBOARDING_SHOWN by lazy { booleanPreferencesKey(name = "twinsOnboardingShown") }
val IS_TANGEM_TOS_ACCEPTED_KEY by lazy { booleanPreferencesKey(name = "tangem_tos_accepted") } val IS_TANGEM_TOS_ACCEPTED_KEY by lazy { booleanPreferencesKey(name = "tangem_tos_accepted") }

View file

@ -1,20 +0,0 @@
package com.tangem.core.featuretoggle.storage
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* Data model with information about feature toggle
*
* @property name feature toggle name
* @property version version number in which the feature will be included
*
* IMPORTANT: if the version is "undefined", it means that feature toggle is disabled!
*
[REDACTED_AUTHOR]
*/
@JsonClass(generateAdapter = true)
internal data class FeatureToggle(
@Json(name = "name") val name: String,
@Json(name = "version") val version: String,
)

View file

@ -1,15 +0,0 @@
package com.tangem.core.featuretoggle.storage
/**
* Component that initializes and stores a list of feature toggles
*
[REDACTED_AUTHOR]
*/
internal interface FeatureTogglesStorage {
/** List of feature toggles */
val featureToggles: List<FeatureToggle>
/** Initialize storage */
suspend fun init()
}

View file

@ -1,29 +0,0 @@
package com.tangem.core.featuretoggle.storage
import com.tangem.core.featuretoggle.storage.LocalFeatureTogglesStorage.Companion.LOCAL_CONFIG_PATH
import com.tangem.datasource.asset.loader.AssetLoader
import kotlin.properties.Delegates
/**
* Storage implementation for storing local feature toggles.
* Feature toggles are declared in file [LOCAL_CONFIG_PATH].
*
* @property assetLoader asset loader
*
[REDACTED_AUTHOR]
*/
internal class LocalFeatureTogglesStorage(
private val assetLoader: AssetLoader,
) : FeatureTogglesStorage {
override var featureToggles: List<FeatureToggle> by Delegates.notNull()
private set
override suspend fun init() {
featureToggles = assetLoader.loadList<FeatureToggle>(LOCAL_CONFIG_PATH)
}
private companion object {
const val LOCAL_CONFIG_PATH: String = "configs/feature_toggles_config"
}
}

View file

@ -1,13 +0,0 @@
package com.tangem.core.featuretoggle.utils
import com.tangem.core.featuretoggle.contract.VersionAvailabilityContract
import com.tangem.core.featuretoggle.storage.FeatureToggle
internal fun List<FeatureToggle>.associateToggles(currentVersion: String): Map<String, Boolean> {
return associate { localToggle ->
Pair(
first = localToggle.name,
second = VersionAvailabilityContract(currentVersion, localToggle.version),
)
}
}

View file

@ -21,7 +21,7 @@ dependencies {
/* Project - Core */ /* Project - Core */
implementation(projects.core.decompose) implementation(projects.core.decompose)
implementation(projects.core.ui) implementation(projects.core.ui)
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.navigation) implementation(projects.core.navigation)
implementation(projects.core.analytics) implementation(projects.core.analytics)
implementation(projects.core.analytics.models) implementation(projects.core.analytics.models)

View file

@ -28,7 +28,7 @@ dependencies {
/** Core modules */ /** Core modules */
implementation(projects.core.ui) implementation(projects.core.ui)
implementation(projects.core.utils) implementation(projects.core.utils)
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.navigation) implementation(projects.core.navigation)
implementation(projects.core.decompose) implementation(projects.core.decompose)
implementation(projects.common.routing) implementation(projects.common.routing)

View file

@ -19,7 +19,7 @@ dependencies {
implementation(projects.core.decompose) implementation(projects.core.decompose)
implementation(projects.core.ui) implementation(projects.core.ui)
implementation(projects.common.routing) implementation(projects.common.routing)
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.analytics) implementation(projects.core.analytics)
/* Project - Domain */ /* Project - Domain */

View file

@ -63,7 +63,7 @@ dependencies {
/* Core */ /* Core */
implementation(projects.core.decompose) implementation(projects.core.decompose)
implementation(projects.core.ui) implementation(projects.core.ui)
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.analytics) implementation(projects.core.analytics)
implementation(projects.core.analytics.models) implementation(projects.core.analytics.models)

View file

@ -17,7 +17,7 @@ dependencies {
implementation(projects.features.manageTokens.api) implementation(projects.features.manageTokens.api)
/** Core modules */ /** Core modules */
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.analytics) implementation(projects.core.analytics)
implementation(projects.core.analytics.models) implementation(projects.core.analytics.models)
implementation(projects.core.utils) implementation(projects.core.utils)

View file

@ -1,6 +1,6 @@
package com.tangem.features.onboarding.v2 package com.tangem.features.onboarding.v2
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import javax.inject.Inject import javax.inject.Inject
internal class DefaultOnboardingV2FeatureToggles @Inject constructor( internal class DefaultOnboardingV2FeatureToggles @Inject constructor(

View file

@ -15,7 +15,7 @@ dependencies {
/** Core modules */ /** Core modules */
implementation(project(":common")) implementation(project(":common"))
implementation(project(":domain:models")) implementation(project(":domain:models"))
implementation(project(":core:featuretoggles")) implementation(project(":core:config-toggles"))
implementation(project(":core:datasource")) implementation(project(":core:datasource"))
implementation(project(":core:analytics")) implementation(project(":core:analytics"))
implementation(projects.core.analytics.models) implementation(projects.core.analytics.models)

View file

@ -21,7 +21,7 @@ dependencies {
/** Project - Core */ /** Project - Core */
implementation(projects.core.decompose) implementation(projects.core.decompose)
implementation(projects.core.ui) implementation(projects.core.ui)
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.navigation) implementation(projects.core.navigation)
/** Project - Common */ /** Project - Common */

View file

@ -1,6 +1,6 @@
package com.tangem.features.onramp package com.tangem.features.onramp
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
internal class DefaultOnrampFeatureToggles( internal class DefaultOnrampFeatureToggles(
private val featureTogglesManager: FeatureTogglesManager, private val featureTogglesManager: FeatureTogglesManager,

View file

@ -1,6 +1,6 @@
package com.tangem.features.onramp package com.tangem.features.onramp
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn

View file

@ -27,7 +27,7 @@ dependencies {
/** Core modules */ /** Core modules */
implementation(projects.core.ui) implementation(projects.core.ui)
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.navigation) implementation(projects.core.navigation)
implementation(projects.core.analytics) implementation(projects.core.analytics)
implementation(projects.core.analytics.models) implementation(projects.core.analytics.models)

View file

@ -43,7 +43,7 @@ dependencies {
implementation(deps.tangem.blockchain) implementation(deps.tangem.blockchain)
/** Core modules */ /** Core modules */
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.ui) implementation(projects.core.ui)
implementation(projects.core.utils) implementation(projects.core.utils)
implementation(projects.core.navigation) implementation(projects.core.navigation)

View file

@ -40,7 +40,7 @@ dependencies {
implementation(deps.tangem.blockchain) implementation(deps.tangem.blockchain)
/** Core modules */ /** Core modules */
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.ui) implementation(projects.core.ui)
implementation(projects.core.utils) implementation(projects.core.utils)
implementation(projects.core.navigation) implementation(projects.core.navigation)

View file

@ -15,7 +15,7 @@ dependencies {
/** Core modules */ /** Core modules */
implementation(projects.core.analytics) implementation(projects.core.analytics)
implementation(projects.core.analytics.models) implementation(projects.core.analytics.models)
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.navigation) implementation(projects.core.navigation)
implementation(projects.core.utils) implementation(projects.core.utils)
implementation(projects.core.ui) implementation(projects.core.ui)

View file

@ -39,7 +39,7 @@ dependencies {
/** Core modules */ /** Core modules */
implementation(projects.core.datasource) implementation(projects.core.datasource)
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.ui) implementation(projects.core.ui)
implementation(projects.core.utils) implementation(projects.core.utils)
implementation(projects.core.navigation) implementation(projects.core.navigation)

View file

@ -5,8 +5,8 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.core.featuretoggle.manager.MutableFeatureTogglesManager import com.tangem.core.configtoggle.feature.MutableFeatureTogglesManager
import com.tangem.core.navigation.finisher.AppFinisher import com.tangem.core.navigation.finisher.AppFinisher
import com.tangem.feature.tester.presentation.featuretoggles.models.TesterFeatureToggle import com.tangem.feature.tester.presentation.featuretoggles.models.TesterFeatureToggle
import com.tangem.feature.tester.presentation.featuretoggles.state.FeatureTogglesContentState import com.tangem.feature.tester.presentation.featuretoggles.state.FeatureTogglesContentState

View file

@ -54,7 +54,7 @@ dependencies {
implementation(projects.core.datasource) implementation(projects.core.datasource)
implementation(projects.core.deepLinks) implementation(projects.core.deepLinks)
implementation(projects.core.deepLinks.global) implementation(projects.core.deepLinks.global)
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.decompose) implementation(projects.core.decompose)
implementation(projects.common.ui) implementation(projects.common.ui)

View file

@ -20,7 +20,7 @@ dependencies {
/* Project - Core */ /* Project - Core */
implementation(projects.core.decompose) implementation(projects.core.decompose)
implementation(projects.core.ui) implementation(projects.core.ui)
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.navigation) implementation(projects.core.navigation)
implementation(projects.core.analytics) implementation(projects.core.analytics)
implementation(projects.core.analytics.models) implementation(projects.core.analytics.models)

View file

@ -46,7 +46,7 @@ dependencies {
kapt(deps.hilt.kapt) kapt(deps.hilt.kapt)
/** Core modules */ /** Core modules */
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.navigation) implementation(projects.core.navigation)
implementation(projects.core.ui) implementation(projects.core.ui)
implementation(projects.core.utils) implementation(projects.core.utils)

View file

@ -1,6 +1,6 @@
package com.tangem.feature.wallet.featuretoggles package com.tangem.feature.wallet.featuretoggles
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import javax.inject.Inject import javax.inject.Inject

View file

@ -13,7 +13,7 @@ dependencies {
// region Core modules // region Core modules
implementation(projects.core.datasource) implementation(projects.core.datasource)
implementation(projects.core.featuretoggles) implementation(projects.core.configToggles)
implementation(projects.core.utils) implementation(projects.core.utils)
// endregion // endregion

View file

@ -9,7 +9,7 @@ import com.tangem.blockchainsdk.datastorage.DefaultBlockchainDataStorage
import com.tangem.blockchainsdk.featuretoggles.DefaultBlockchainSDKFeatureToggles import com.tangem.blockchainsdk.featuretoggles.DefaultBlockchainSDKFeatureToggles
import com.tangem.blockchainsdk.loader.BlockchainProvidersResponseLoader import com.tangem.blockchainsdk.loader.BlockchainProvidersResponseLoader
import com.tangem.blockchainsdk.store.DefaultRuntimeStore import com.tangem.blockchainsdk.store.DefaultRuntimeStore
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.AppPreferencesStore

View file

@ -1,6 +1,6 @@
package com.tangem.blockchainsdk.featuretoggles package com.tangem.blockchainsdk.featuretoggles
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
internal class DefaultBlockchainSDKFeatureToggles( internal class DefaultBlockchainSDKFeatureToggles(
private val featureTogglesManager: FeatureTogglesManager, private val featureTogglesManager: FeatureTogglesManager,

View file

@ -127,7 +127,7 @@ include(":common:routing")
include(":core:analytics") include(":core:analytics")
include(":core:analytics:models") include(":core:analytics:models")
include(":core:datasource") include(":core:datasource")
include(":core:featuretoggles") include(":core:config-toggles")
include(":core:navigation") include(":core:navigation")
include(":core:res") include(":core:res")
include(":core:ui") include(":core:ui")