Updated on 2026-08-14
This commit is contained in:
parent
2ea7bbdb77
commit
cca12971ee
12 changed files with 117 additions and 120 deletions
|
|
@ -3,7 +3,6 @@ apply plugin: 'kotlin-android'
|
|||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'com.google.firebase.crashlytics'
|
||||
//apply plugin: 'com.google.firebase.firebase-perf'
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
|
||||
android {
|
||||
|
|
@ -25,6 +24,7 @@ android {
|
|||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
signingConfig signingConfigs.debug
|
||||
buildConfigField 'String', 'CONFIG_ENVIRONMENT', '\"prod\"'
|
||||
}
|
||||
debug {
|
||||
debuggable true
|
||||
|
|
@ -34,11 +34,13 @@ android {
|
|||
firebaseCrashlytics {
|
||||
mappingFileUploadEnabled false
|
||||
}
|
||||
buildConfigField 'String', 'CONFIG_ENVIRONMENT', '\"dev\"'
|
||||
}
|
||||
debug_beta {
|
||||
initWith debug
|
||||
versionNameSuffix "-beta"
|
||||
applicationIdSuffix ".debug"
|
||||
buildConfigField 'String', 'CONFIG_ENVIRONMENT', '\"dev\"'
|
||||
}
|
||||
}
|
||||
kotlinOptions {
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
[
|
||||
{
|
||||
"name": "coinMarketCapKey",
|
||||
"value": "f6622117-c043-47a0-8975-9d673ce484de"
|
||||
},
|
||||
{
|
||||
"name": "moonPayApiKey",
|
||||
"value": "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE"
|
||||
},
|
||||
{
|
||||
"name": "moonPayApiSecretKey",
|
||||
"value": "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
[
|
||||
{
|
||||
"name": "payIdIsEnabled",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "usePayId",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "useTopUp",
|
||||
"value": false
|
||||
}
|
||||
]
|
||||
10
app/src/main/assets/features_dev.json
Normal file
10
app/src/main/assets/features_dev.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[
|
||||
{
|
||||
"name": "isPayIdCreationEnabled",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"name": "isTopUpEnabled",
|
||||
"value": false
|
||||
}
|
||||
]
|
||||
10
app/src/main/assets/features_prod.json
Normal file
10
app/src/main/assets/features_prod.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[
|
||||
{
|
||||
"name": "isPayIdCreationEnabled",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"name": "isTopUpEnabled",
|
||||
"value": false
|
||||
}
|
||||
]
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
[
|
||||
{
|
||||
"name": "payIdIsEnabled",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "usePayId",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "useTopUp",
|
||||
"value": false
|
||||
}
|
||||
]
|
||||
|
|
@ -9,7 +9,6 @@ import com.tangem.tap.common.redux.AppState
|
|||
import com.tangem.tap.common.redux.appReducer
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.config.ConfigManager
|
||||
import com.tangem.tap.domain.config.ConfigNameResolver
|
||||
import com.tangem.tap.domain.config.LocalLoader
|
||||
import com.tangem.tap.domain.config.RemoteLoader
|
||||
import com.tangem.tap.network.NetworkConnectivity
|
||||
|
|
@ -51,10 +50,8 @@ class TapApplication : Application() {
|
|||
|
||||
private fun loadConfigs() {
|
||||
val moshi = createMoshi()
|
||||
val nameResolver = ConfigNameResolver.get()
|
||||
|
||||
val localLoader = LocalLoader(this, nameResolver, moshi)
|
||||
val remoteLoader = RemoteLoader(nameResolver, moshi)
|
||||
val localLoader = LocalLoader(this, moshi)
|
||||
val remoteLoader = RemoteLoader(moshi)
|
||||
val configManager = ConfigManager(localLoader, remoteLoader)
|
||||
configManager.load { store.dispatch(GlobalAction.SetConfigManager(configManager)) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import TapWorkarounds
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.commands.CardStatus
|
||||
|
|
@ -10,6 +11,7 @@ import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
|||
import com.tangem.tap.common.redux.global.CryptoCurrencyName
|
||||
import com.tangem.tap.common.redux.global.FiatCurrencyName
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.config.ConfigManager
|
||||
import com.tangem.tap.domain.extensions.amountToCreateAccount
|
||||
import com.tangem.tap.domain.extensions.isNoAccountError
|
||||
import com.tangem.tap.domain.tasks.ScanNoteResponse
|
||||
|
|
@ -25,6 +27,7 @@ import java.math.BigDecimal
|
|||
class TapWalletManager {
|
||||
private val payIdManager = PayIdManager()
|
||||
private val coinMarketCapService = CoinMarketCapService()
|
||||
private var tapWorkarounds: TapWorkarounds? = null
|
||||
|
||||
suspend fun loadWalletData() {
|
||||
val walletManager = store.state.globalState.scanNoteResponse?.walletManager
|
||||
|
|
@ -82,7 +85,12 @@ class TapWalletManager {
|
|||
if (addAnalyticsEvent) {
|
||||
FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card)
|
||||
}
|
||||
store.state.globalState.configManager?.onCardScanned(data.card)
|
||||
tapWorkarounds = TapWorkarounds(data.card)
|
||||
if (tapWorkarounds!!.isStart2Coin) {
|
||||
store.state.globalState.configManager?.turnOf(ConfigManager.isPayIdCreationEnabled)
|
||||
} else {
|
||||
store.state.globalState.configManager?.resetToDefault(ConfigManager.isPayIdCreationEnabled)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
store.dispatch(WalletAction.ResetState)
|
||||
store.dispatch(GlobalAction.SaveScanNoteResponse(data))
|
||||
|
|
@ -99,11 +107,11 @@ class TapWalletManager {
|
|||
store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection))
|
||||
return@withContext
|
||||
}
|
||||
val config = store.state.globalState.configManager?.config?: return@withContext
|
||||
val config = store.state.globalState.configManager?.config ?: return@withContext
|
||||
|
||||
store.dispatch(WalletAction.LoadWallet(
|
||||
data.walletManager.wallet, data.verifyResponse?.artworkInfo?.id,
|
||||
!config.isStart2Coin && config.useTopUp
|
||||
tapWorkarounds?.isStart2Coin == false && config.isTopUpEnabled
|
||||
))
|
||||
store.dispatch(WalletAction.LoadArtwork(data.card, artworkId))
|
||||
store.dispatch(WalletAction.LoadFiatRate)
|
||||
|
|
@ -150,10 +158,9 @@ class TapWalletManager {
|
|||
|
||||
|
||||
private suspend fun loadPayIdIfNeeded(): Result<String?>? {
|
||||
val config = store.state.globalState.configManager?.config
|
||||
val scanNoteResponse = store.state.globalState.scanNoteResponse
|
||||
if (config?.usePayId == false ||
|
||||
scanNoteResponse?.walletManager?.wallet?.blockchain?.isPayIdSupported() == false) {
|
||||
if (store.state.globalState.configManager?.config?.isPayIdCreationEnabled == false
|
||||
|| scanNoteResponse?.walletManager?.wallet?.blockchain?.isPayIdSupported() == false) {
|
||||
return null
|
||||
}
|
||||
val cardId = scanNoteResponse?.card?.cardId
|
||||
|
|
@ -168,12 +175,12 @@ class TapWalletManager {
|
|||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
val config = store.state.globalState.configManager?.config?: return@withContext
|
||||
val payId = result.data
|
||||
if (!config.payIdIsEnabled) {
|
||||
val config = store.state.globalState.configManager?.config
|
||||
if (config?.isPayIdCreationEnabled == false) {
|
||||
store.dispatch(WalletAction.DisablePayId)
|
||||
return@withContext
|
||||
}
|
||||
val payId = result.data
|
||||
if (payId == null) {
|
||||
store.dispatch(WalletAction.LoadPayId.NotCreated)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
import com.tangem.commands.Card
|
||||
|
||||
class TapWorkarounds(val card: Card) {
|
||||
|
||||
val isStart2Coin: Boolean = card.cardData?.issuerName == START_2_COIN_ISSUER
|
||||
|
||||
fun isPayisPayIdEnabled(): Boolean {
|
||||
if (isStart2Coin) return false
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val START_2_COIN_ISSUER = "start2coin"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.domain.config
|
||||
|
||||
import android.content.Context
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.google.firebase.ktx.Firebase
|
||||
import com.google.firebase.remoteconfig.ktx.remoteConfig
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
|
|
@ -14,40 +15,16 @@ import timber.log.Timber
|
|||
*/
|
||||
interface ConfigLoader {
|
||||
fun loadConfig(onComplete: (ConfigModel) -> Unit)
|
||||
}
|
||||
|
||||
|
||||
interface NameResolver {
|
||||
fun getFeaturesName(): String
|
||||
fun getConfigValuesName(): String
|
||||
}
|
||||
|
||||
class ConfigNameResolver {
|
||||
companion object {
|
||||
fun get(): NameResolver = if (BuildConfig.DEBUG) dev() else prod()
|
||||
|
||||
private fun dev(): NameResolver {
|
||||
return object : NameResolver {
|
||||
override fun getFeaturesName(): String = "dev_features"
|
||||
override fun getConfigValuesName(): String = "dev_config_values"
|
||||
}
|
||||
}
|
||||
|
||||
private fun prod(): NameResolver {
|
||||
return object : NameResolver {
|
||||
override fun getFeaturesName(): String = "prod_features"
|
||||
override fun getConfigValuesName(): String = "prod_config_values"
|
||||
|
||||
}
|
||||
}
|
||||
const val featuresName = "features_${BuildConfig.CONFIG_ENVIRONMENT}"
|
||||
const val configValuesName = "config_${BuildConfig.CONFIG_ENVIRONMENT}"
|
||||
}
|
||||
}
|
||||
|
||||
class KeyValueModel<T>(val name: String, value: T?)
|
||||
|
||||
class LocalLoader(
|
||||
private val context: Context,
|
||||
private val nameResolver: NameResolver,
|
||||
private val moshi: Moshi
|
||||
) : ConfigLoader {
|
||||
|
||||
|
|
@ -58,8 +35,8 @@ class LocalLoader(
|
|||
val valuesType = Types.newParameterizedType(List::class.java, ConfigValueModel::class.java)
|
||||
val valuesAdapter: JsonAdapter<List<ConfigValueModel>> = moshi.adapter(valuesType)
|
||||
|
||||
val jsonFeatures = readAssetAsString(nameResolver.getFeaturesName())
|
||||
val jsonConfigValues = readAssetAsString(nameResolver.getConfigValuesName())
|
||||
val jsonFeatures = readAssetAsString(ConfigLoader.featuresName)
|
||||
val jsonConfigValues = readAssetAsString(ConfigLoader.configValuesName)
|
||||
|
||||
ConfigModel(featureAdapter.fromJson(jsonFeatures) ?: listOf(),
|
||||
valuesAdapter.fromJson(jsonConfigValues) ?: listOf())
|
||||
|
|
@ -76,7 +53,6 @@ class LocalLoader(
|
|||
}
|
||||
|
||||
class RemoteLoader(
|
||||
private val nameResolver: NameResolver,
|
||||
private val moshi: Moshi
|
||||
) : ConfigLoader {
|
||||
|
||||
|
|
@ -85,7 +61,7 @@ class RemoteLoader(
|
|||
val remoteConfig = Firebase.remoteConfig
|
||||
remoteConfig.fetchAndActivate().addOnCompleteListener {
|
||||
if (it.isSuccessful) {
|
||||
val config = remoteConfig.getValue(nameResolver.getFeaturesName())
|
||||
val config = remoteConfig.getValue(ConfigLoader.featuresName)
|
||||
val jsonConfig = config.asString()
|
||||
if (jsonConfig.isEmpty()) {
|
||||
onComplete(emptyConfig)
|
||||
|
|
@ -98,7 +74,7 @@ class RemoteLoader(
|
|||
onComplete(emptyConfig)
|
||||
}
|
||||
}.addOnFailureListener {
|
||||
Timber.e(it)
|
||||
FirebaseCrashlytics.getInstance().recordException(it)
|
||||
onComplete(emptyConfig)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,68 +1,90 @@
|
|||
package com.tangem.tap.domain.config
|
||||
|
||||
import com.tangem.commands.Card
|
||||
import com.tangem.tangem_sdk_new.ui.animation.VoidCallback
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class ConfigState(
|
||||
data class Config(
|
||||
val coinMarketCapKey: String = "f6622117-c043-47a0-8975-9d673ce484de",
|
||||
val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE",
|
||||
val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C",
|
||||
val payIdIsEnabled: Boolean = true,
|
||||
val usePayId: Boolean = true,
|
||||
val useTopUp: Boolean = false,
|
||||
val isStart2Coin: Boolean = false
|
||||
val isPayIdCreationEnabled: Boolean = true,
|
||||
val isTopUpEnabled: Boolean = false,
|
||||
)
|
||||
|
||||
class ConfigManager(
|
||||
private val localLoader: ConfigLoader,
|
||||
private val remoteLoader: ConfigLoader
|
||||
) {
|
||||
private val usePayId = "usePayId"
|
||||
private val payIdIsEnabled = "payIdIsEnabled"
|
||||
private val useTopUp = "useTopUp"
|
||||
private val isStart2Coin = "isStart2Coin"
|
||||
private val coinMarketCapKey = "coinMarketCapKey"
|
||||
private val moonPayApiKey = "moonPayApiKey"
|
||||
private val moonPayApiSecretKey = "moonPayApiSecretKey"
|
||||
|
||||
var config: ConfigState = ConfigState()
|
||||
var config: Config = Config()
|
||||
private set
|
||||
|
||||
private var defaultConfig = Config()
|
||||
|
||||
fun load(onComplete: VoidCallback? = null) {
|
||||
localLoader.loadConfig {config ->
|
||||
config.features?.forEach { updateFeature(it.name, it.value) }
|
||||
config.configValues?.forEach { updateKeys(it.name, it.value) }
|
||||
localLoader.loadConfig { config ->
|
||||
config.features?.forEach { setupFeature(it.name, it.value) }
|
||||
config.configValues?.forEach { setupKey(it.name, it.value) }
|
||||
}
|
||||
remoteLoader.loadConfig {
|
||||
it.features?.forEach { feature -> updateFeature(feature.name, feature.value) }
|
||||
remoteLoader.loadConfig { config ->
|
||||
config.features?.forEach { setupFeature(it.name, it.value) }
|
||||
onComplete?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
fun onCardScanned(card: Card) {
|
||||
updateFeature(isStart2Coin, card.cardData?.issuerName?.toLowerCase(Locale.US) == "start2coin")
|
||||
fun turnOf(name: String) {
|
||||
when (name) {
|
||||
isPayIdCreationEnabled -> config = config.copy(isPayIdCreationEnabled = false)
|
||||
isTopUpEnabled -> config = config.copy(isTopUpEnabled = false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFeature(name: String, value: Boolean?) {
|
||||
fun resetToDefault(name: String) {
|
||||
when (name) {
|
||||
isPayIdCreationEnabled -> config = config.copy(isPayIdCreationEnabled = defaultConfig.isPayIdCreationEnabled)
|
||||
isTopUpEnabled -> config = config.copy(isTopUpEnabled = defaultConfig.isTopUpEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFeature(name: String, value: Boolean) {
|
||||
val newValue = value ?: return
|
||||
|
||||
when (name) {
|
||||
usePayId -> config = config.copy(usePayId = newValue)
|
||||
payIdIsEnabled -> config = config.copy(payIdIsEnabled = newValue)
|
||||
useTopUp -> config = config.copy(useTopUp = newValue)
|
||||
isStart2Coin -> config = config.copy(isStart2Coin = newValue)
|
||||
isPayIdCreationEnabled -> {
|
||||
config = config.copy(isPayIdCreationEnabled = newValue)
|
||||
defaultConfig = defaultConfig.copy(isPayIdCreationEnabled = newValue)
|
||||
}
|
||||
isTopUpEnabled -> {
|
||||
config = config.copy(isTopUpEnabled = newValue)
|
||||
defaultConfig = defaultConfig.copy(isTopUpEnabled = newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateKeys(name: String, value: String) {
|
||||
private fun setupKey(name: String, value: String) {
|
||||
when (name) {
|
||||
coinMarketCapKey -> config = config.copy(coinMarketCapKey = value)
|
||||
moonPayApiKey -> config = config.copy(moonPayApiKey = value)
|
||||
moonPayApiSecretKey -> config = config.copy(moonPayApiSecretKey = value)
|
||||
coinMarketCapKey -> {
|
||||
config = config.copy(coinMarketCapKey = value)
|
||||
defaultConfig = defaultConfig.copy(coinMarketCapKey = value)
|
||||
}
|
||||
moonPayApiKey -> {
|
||||
config = config.copy(moonPayApiKey = value)
|
||||
defaultConfig = defaultConfig.copy(moonPayApiKey = value)
|
||||
}
|
||||
moonPayApiSecretKey -> {
|
||||
config = config.copy(moonPayApiSecretKey = value)
|
||||
defaultConfig = defaultConfig.copy(moonPayApiSecretKey = value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val isPayIdCreationEnabled = "isPayIdCreationEnabled"
|
||||
const val isTopUpEnabled = "useTopUp"
|
||||
const val coinMarketCapKey = "coinMarketCapKey"
|
||||
const val moonPayApiKey = "moonPayApiKey"
|
||||
const val moonPayApiSecretKey = "moonPayApiSecretKey"
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ interface BaseConfigModel<V> {
|
|||
|
||||
class FeatureModel(
|
||||
override val name: String,
|
||||
override val value: Boolean?
|
||||
override val value: Boolean
|
||||
) : BaseConfigModel<Boolean>
|
||||
|
||||
class ConfigValueModel(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue