Updated on 2026-08-14

This commit is contained in:
Tangem 2023-02-10 15:29:38 +03:00
parent 0e1d6547d1
commit 79d823b94f
32 changed files with 156 additions and 110 deletions

View file

@ -0,0 +1,24 @@
package com.tangem.datasource.config
import com.tangem.datasource.config.models.Config
import com.tangem.datasource.config.models.ConfigModel
/**
[REDACTED_AUTHOR]
*/
interface ConfigManager {
val config: Config
fun load(configLoader: Loader<ConfigModel>, onComplete: ((config: Config) -> Unit)? = null)
fun turnOff(name: String)
fun resetToDefault(name: String)
companion object {
const val IS_SENDING_TO_PAY_ID_ENABLED = "isSendingToPayIdEnabled"
const val IS_CREATING_TWIN_CARDS_ALLOWED = "isCreatingTwinCardsAllowed"
const val IS_TOP_UP_ENABLED = "isTopUpEnabled"
}
}

View file

@ -0,0 +1,102 @@
package com.tangem.datasource.config
import com.tangem.blockchain.common.BlockchainSdkConfig
import com.tangem.blockchain.common.BlockchairCredentials
import com.tangem.blockchain.common.QuickNodeCredentials
import com.tangem.datasource.config.ConfigManager.Companion.IS_CREATING_TWIN_CARDS_ALLOWED
import com.tangem.datasource.config.ConfigManager.Companion.IS_SENDING_TO_PAY_ID_ENABLED
import com.tangem.datasource.config.ConfigManager.Companion.IS_TOP_UP_ENABLED
import com.tangem.datasource.config.models.Config
import com.tangem.datasource.config.models.ConfigModel
import com.tangem.datasource.config.models.ConfigValueModel
import com.tangem.datasource.config.models.FeatureModel
import javax.inject.Inject
internal class ConfigManagerImpl @Inject constructor() : ConfigManager {
override var config: Config = Config()
private set
private var defaultConfig = Config()
override fun load(configLoader: Loader<ConfigModel>, onComplete: ((config: Config) -> Unit)?) {
configLoader.load { configModel ->
setupFeature(configModel.features)
setupConfigValues(configModel.configValues)
onComplete?.invoke(config)
}
}
override fun turnOff(name: String) {
when (name) {
IS_SENDING_TO_PAY_ID_ENABLED -> config = config.copy(isSendingToPayIdEnabled = false)
IS_TOP_UP_ENABLED -> config = config.copy(isTopUpEnabled = false)
IS_CREATING_TWIN_CARDS_ALLOWED -> config = config.copy(isCreatingTwinCardsAllowed = false)
}
}
override fun resetToDefault(name: String) {
when (name) {
IS_SENDING_TO_PAY_ID_ENABLED -> config =
config.copy(isSendingToPayIdEnabled = defaultConfig.isSendingToPayIdEnabled)
IS_TOP_UP_ENABLED -> config = config.copy(isTopUpEnabled = defaultConfig.isTopUpEnabled)
IS_CREATING_TWIN_CARDS_ALLOWED -> config =
config.copy(isCreatingTwinCardsAllowed = defaultConfig.isCreatingTwinCardsAllowed)
}
}
private fun setupFeature(featureModel: FeatureModel?) {
val model = featureModel ?: return
config = config.copy(
isTopUpEnabled = model.isTopUpEnabled,
isSendingToPayIdEnabled = model.isSendingToPayIdEnabled,
isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed,
)
defaultConfig = defaultConfig.copy(
isTopUpEnabled = model.isTopUpEnabled,
isSendingToPayIdEnabled = model.isSendingToPayIdEnabled,
isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed,
)
}
private fun setupConfigValues(configValues: ConfigValueModel?) {
val values = configValues ?: return
config = createConfig(config, values)
defaultConfig = config.copy()
}
private fun createConfig(config: Config, configValues: ConfigValueModel): Config {
return config.copy(
coinMarketCapKey = configValues.coinMarketCapKey,
moonPayApiKey = configValues.moonPayApiKey,
moonPayApiSecretKey = configValues.moonPayApiSecretKey,
mercuryoWidgetId = configValues.mercuryoWidgetId,
mercuryoSecret = configValues.mercuryoSecret,
blockchainSdkConfig = BlockchainSdkConfig(
blockchairCredentials = BlockchairCredentials(
apiKey = configValues.blockchairApiKeys,
authToken = configValues.blockchairAuthorizationToken,
),
blockcypherTokens = configValues.blockcypherTokens,
quickNodeCredentials = QuickNodeCredentials(
apiKey = configValues.quiknodeApiKey,
subdomain = configValues.quiknodeSubdomain,
),
bscQuickNodeCredentials = QuickNodeCredentials(
apiKey = configValues.bscQuiknodeApiKey,
subdomain = configValues.bscQuiknodeSubdomain,
),
infuraProjectId = configValues.infuraProjectId,
tronGridApiKey = configValues.tronGridApiKey,
saltPayAuthToken = configValues.saltPay.credentials.token,
),
appsFlyerDevKey = configValues.appsFlyer.appsFlyerDevKey,
amplitudeApiKey = configValues.amplitudeApiKey,
shopify = configValues.shopifyShop,
zendesk = configValues.zendesk,
saltPayConfig = configValues.saltPay,
)
}
}

View file

@ -0,0 +1,38 @@
package com.tangem.datasource.config
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.tangem.datasource.config.models.ConfigModel
import com.tangem.datasource.config.models.ConfigValueModel
import com.tangem.datasource.config.models.FeatureModel
import com.tangem.datasource.utils.AssetReader
import timber.log.Timber
/**
[REDACTED_AUTHOR]
*/
class FeaturesLocalLoader(
private val assetReader: AssetReader,
private val moshi: Moshi,
buildEnvironment: String,
) : Loader<ConfigModel> {
private val featuresName = "features_$buildEnvironment"
private val configValuesName = "tangem-app-config/config_$buildEnvironment"
override fun load(onComplete: (ConfigModel) -> Unit) {
val config = try {
val featureAdapter: JsonAdapter<FeatureModel> = moshi.adapter(FeatureModel::class.java)
val valuesAdapter: JsonAdapter<ConfigValueModel> = moshi.adapter(ConfigValueModel::class.java)
val jsonFeatures = assetReader.readAssetAsString(featuresName)
val jsonConfigValues = assetReader.readAssetAsString(configValuesName)
ConfigModel(featureAdapter.fromJson(jsonFeatures), valuesAdapter.fromJson(jsonConfigValues))
} catch (ex: Exception) {
Timber.e(ex)
ConfigModel.empty()
}
onComplete(config)
}
}

View file

@ -0,0 +1,8 @@
package com.tangem.datasource.config
/**
[REDACTED_AUTHOR]
*/
interface Loader<T> {
fun load(onComplete: (T) -> Unit)
}

View file

@ -0,0 +1,21 @@
package com.tangem.datasource.config.models
import com.tangem.blockchain.common.BlockchainSdkConfig
data class Config(
val coinMarketCapKey: String = "f6622117-c043-47a0-8975-9d673ce484de",
val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE",
val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C",
val mercuryoWidgetId: String = "",
val mercuryoSecret: String = "",
val appsFlyerDevKey: String = "",
val amplitudeApiKey: String = "",
val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(),
val isSendingToPayIdEnabled: Boolean = true,
val isTopUpEnabled: Boolean = false,
@Deprecated("Not relevant since version 3.23")
val isCreatingTwinCardsAllowed: Boolean = false,
val shopify: ShopifyShop? = null,
val zendesk: ZendeskConfig? = null,
val saltPayConfig: SaltPayConfig? = null,
)

View file

@ -0,0 +1,49 @@
package com.tangem.datasource.config.models
/**
[REDACTED_AUTHOR]
*/
class FeatureModel(
val isWalletPayIdEnabled: Boolean,
val isTopUpEnabled: Boolean,
val isSendingToPayIdEnabled: Boolean,
val isCreatingTwinCardsAllowed: Boolean,
)
@Suppress("LongParameterList")
class ConfigValueModel(
val coinMarketCapKey: String,
val mercuryoWidgetId: String,
val mercuryoSecret: String,
val moonPayApiKey: String,
val moonPayApiSecretKey: String,
val blockchairApiKeys: List<String>,
val blockchairAuthorizationToken: String?,
val quiknodeSubdomain: String,
val quiknodeApiKey: String,
val bscQuiknodeSubdomain: String,
val bscQuiknodeApiKey: String,
val blockcypherTokens: Set<String>?,
val infuraProjectId: String?,
val appsFlyer: AppsFlyer,
val shopifyShop: ShopifyShop?,
val zendesk: ZendeskConfig?,
val saltPay: SaltPayConfig,
val tronGridApiKey: String,
val amplitudeApiKey: String,
)
data class AppsFlyer(
val appsFlyerDevKey: String,
val appsFlyerAppID: String,
)
class ConfigModel(
val features: FeatureModel?,
val configValues: ConfigValueModel?,
) {
companion object {
fun empty(): ConfigModel = ConfigModel(null, null)
}
}

View file

@ -0,0 +1,36 @@
package com.tangem.datasource.config.models
import org.spongycastle.util.encoders.Base64.toBase64String
/**
[REDACTED_AUTHOR]
*/
data class SaltPayConfig(
val zendesk: ZendeskConfig,
val kycProvider: KYCProvider,
val credentials: Credentials,
) {
companion object {
fun stub(): SaltPayConfig {
return SaltPayConfig(
zendesk = ZendeskConfig("", "", "", "", ""),
kycProvider = KYCProvider("", "", "", ""),
credentials = Credentials("", ""),
)
}
}
}
data class KYCProvider(
val baseUrl: String,
val externalIdParameterKey: String,
val sidParameterKey: String,
val sidValue: String,
)
data class Credentials(
val user: String,
val password: String,
) {
val token: String by lazy { "Basic ${toBase64String("$user:$password".toByteArray())}" }
}

View file

@ -0,0 +1,12 @@
package com.tangem.datasource.config.models
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class ShopifyShop(
val domain: String,
@Json(name = "storefrontApiKeyAndroid")
val storefrontApiKey: String,
val merchantID: String,
)

View file

@ -0,0 +1,18 @@
package com.tangem.datasource.config.models
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class ZendeskConfig(
@Json(name = "zendeskApiKey")
val apiKey: String,
@Json(name = "zendeskAppId")
val appId: String,
@Json(name = "zendeskClientId")
val clientId: String,
@Json(name = "zendeskAccountKey")
val accountKey: String,
@Json(name = "zendeskUrl")
val url: String,
)

View file

@ -0,0 +1,16 @@
package com.tangem.datasource.di
import com.tangem.datasource.config.ConfigManager
import com.tangem.datasource.config.ConfigManagerImpl
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
internal interface ConfigModule {
@Binds
fun bindConfigManager(configManager: ConfigManagerImpl): ConfigManager
}

View file

@ -0,0 +1,23 @@
package com.tangem.datasource.utils
import android.content.Context
/**
[REDACTED_AUTHOR]
*/
interface AssetReader {
fun readAssetAsString(name: String): String
}
class AndroidAssetReader(
private val context: Context,
) : AssetReader {
override fun readAssetAsString(name: String): String {
return context.readAssetAsString(name)
}
}
fun Context.readAssetAsString(fileName: String): String {
return this.assets.open("$fileName.json").bufferedReader().readText()
}