diff --git a/app/build.gradle b/app/build.gradle index de5564ec62..ff677eeba7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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,6 +34,7 @@ android { firebaseCrashlytics { mappingFileUploadEnabled false } + buildConfigField 'String', 'CONFIG_ENVIRONMENT', '\"dev\"' } debug_beta { initWith debug @@ -70,9 +71,9 @@ dependencies { implementation 'com.google.android.material:material:1.2.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' - implementation 'com.tangem:blockchain:1.108.0' - implementation 'com.tangem:core:1.80.0' - implementation 'com.tangem:sdk:1.80.0' + implementation 'com.tangem:blockchain:1.117.0' + implementation 'com.tangem:core:1.83.0' + implementation 'com.tangem:sdk:1.83.0' // WebView implementation "androidx.browser:browser:1.2.0" @@ -98,6 +99,7 @@ dependencies { // Firebase implementation platform('com.google.firebase:firebase-bom:26.0.0') implementation 'com.google.firebase:firebase-crashlytics' + implementation 'com.google.firebase:firebase-config-ktx' implementation 'com.google.firebase:firebase-analytics-ktx' testImplementation 'junit:junit:4.13' diff --git a/app/src/main/assets/features_dev.json b/app/src/main/assets/features_dev.json new file mode 100644 index 0000000000..33cf6e331b --- /dev/null +++ b/app/src/main/assets/features_dev.json @@ -0,0 +1,10 @@ +[ + { + "name": "isWalletPayIdEnabled", + "value": true + }, + { + "name": "isTopUpEnabled", + "value": true + } +] \ No newline at end of file diff --git a/app/src/main/assets/features_prod.json b/app/src/main/assets/features_prod.json new file mode 100644 index 0000000000..ede61eb088 --- /dev/null +++ b/app/src/main/assets/features_prod.json @@ -0,0 +1,10 @@ +[ + { + "name": "isWalletPayIdEnabled", + "value": false + }, + { + "name": "isTopUpEnabled", + "value": false + } +] \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index b19bfc730e..5e43828af0 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -1,10 +1,18 @@ package com.tangem.tap import android.app.Application +import com.google.firebase.ktx.Firebase +import com.google.firebase.remoteconfig.ktx.remoteConfig +import com.google.firebase.remoteconfig.ktx.remoteConfigSettings import com.tangem.tap.common.images.PicassoHelper 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.LocalLoader +import com.tangem.tap.domain.config.RemoteLoader import com.tangem.tap.network.NetworkConnectivity +import com.tangem.tap.network.createMoshi import com.tangem.tap.persistence.PreferencesStorage import com.tangem.wallet.BuildConfig import org.rekotlin.Store @@ -20,11 +28,31 @@ lateinit var preferencesStorage: PreferencesStorage class TapApplication : Application() { override fun onCreate() { super.onCreate() + if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) + Firebase.remoteConfig.setConfigSettingsAsync(remoteConfigSettings { + this.minimumFetchIntervalInSeconds = 60 + }) + } else { + Firebase.remoteConfig.setConfigSettingsAsync(remoteConfigSettings { + this.minimumFetchIntervalInSeconds = 3600 + }) } + NetworkConnectivity.createInstance(store, this) preferencesStorage = PreferencesStorage(this) PicassoHelper.initPicassoWithCaching(this) + + loadConfigs() + } + + + private fun loadConfigs() { + val moshi = createMoshi() + val localLoader = LocalLoader(this, moshi) + val remoteLoader = RemoteLoader(moshi) + val configManager = ConfigManager(localLoader, remoteLoader) + configManager.load { store.dispatch(GlobalAction.SetConfigManager(configManager)) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/TapConfig.kt b/app/src/main/java/com/tangem/tap/TapConfig.kt deleted file mode 100644 index 219dbfb94a..0000000000 --- a/app/src/main/java/com/tangem/tap/TapConfig.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.tangem.tap - -object TapConfig { - const val usePayId: Boolean = true - const val useTopUp: Boolean = true - const val coinMarketCapKey = "f6622117-c043-47a0-8975-9d673ce484de" - const val moonPayApiKey = "pk_live_YTDl4Uei4411FXLAFe2rn8R2qWSiQcm" - const val moonPayApiSecretKey = "sk_live_7wMjoyGTGAB3i1suQHszCWRRqJBRPsQF" -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index c0e641bffe..9fb73bdfcb 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -1,5 +1,6 @@ package com.tangem.tap.common.redux.global +import com.tangem.tap.domain.config.ConfigManager import com.tangem.tap.domain.tasks.ScanNoteResponse import org.rekotlin.Action import java.math.BigDecimal @@ -15,4 +16,5 @@ sealed class GlobalAction : Action { data class Success(val appCurrency: FiatCurrencyName) : GlobalAction() } data class UpdateWalletSignedHashes(val walletSignedHashes: Int?) : GlobalAction() + data class SetConfigManager(val configManager: ConfigManager) : GlobalAction() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index 66fbb80140..2b27751177 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -33,6 +33,9 @@ fun globalReducer(action: Action, state: AppState): GlobalState { globalState } } + is GlobalAction.SetConfigManager -> { + globalState.copy(configManager = action.configManager) + } else -> globalState } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index 9da6906d9c..f9e05cdeac 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -4,6 +4,7 @@ import com.tangem.commands.common.network.TangemService import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY import com.tangem.tap.domain.PayIdManager import com.tangem.tap.domain.TapWalletManager +import com.tangem.tap.domain.config.ConfigManager import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.network.coinmarketcap.CoinMarketCapService import org.rekotlin.StateType @@ -16,6 +17,7 @@ data class GlobalState( val coinMarketCapService: CoinMarketCapService = CoinMarketCapService(), val tangemService: TangemService = TangemService(), val conversionRates: ConversionRates = ConversionRates(emptyMap()), + val configManager: ConfigManager? = null, val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY ) : StateType diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 622c29ef40..c6e9bae5c8 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -1,19 +1,21 @@ package com.tangem.tap.domain +import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.WalletManager import com.tangem.commands.CardStatus import com.tangem.commands.common.network.Result import com.tangem.common.extensions.toHexString -import com.tangem.tap.TapConfig import com.tangem.tap.common.analytics.AnalyticsEvent 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 +import com.tangem.tap.features.send.redux.AddressPayIdActionUi import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.NetworkConnectivity import com.tangem.tap.network.coinmarketcap.CoinMarketCapService @@ -65,17 +67,13 @@ class TapWalletManager { } suspend fun loadFiatRate(fiatCurrency: FiatCurrencyName) { - val wallet = store.state.globalState.scanNoteResponse?.walletManager?.wallet - val blockchainCurrency = wallet?.blockchain?.currency - val tokenCurrency = wallet?.token?.symbol + val wallet = store.state.globalState.scanNoteResponse?.walletManager?.wallet ?: return - val blockchainRate = blockchainCurrency?.let { coinMarketCapService.getRate(it, fiatCurrency) } - val tokenRate = tokenCurrency?.let { coinMarketCapService.getRate(it, fiatCurrency) } + val currencyList = wallet.getTokens().map { it.symbol }.toMutableList() + currencyList.add(wallet.blockchain.currency) val results = mutableListOf?>>() - if (blockchainCurrency != null) results.add(blockchainCurrency to blockchainRate) - if (tokenCurrency != null) results.add(tokenCurrency to tokenRate) - + currencyList.forEach { results.add(it to coinMarketCapService.getRate(it, fiatCurrency)) } handleFiatRatesResult(results) } @@ -84,9 +82,18 @@ class TapWalletManager { FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card) } TapWorkarounds.updateCard(data.card) + val configManager = store.state.globalState.configManager + if (TapWorkarounds.isStart2Coin) { + configManager?.turnOff(ConfigManager.isWalletPayIdEnabled) + configManager?.turnOff(ConfigManager.isTopUpEnabled) + } else { + configManager?.resetToDefault(ConfigManager.isWalletPayIdEnabled) + configManager?.resetToDefault(ConfigManager.isTopUpEnabled) + } withContext(Dispatchers.Main) { store.dispatch(WalletAction.ResetState) store.dispatch(GlobalAction.SaveScanNoteResponse(data)) + store.dispatch(AddressPayIdActionUi.ChangePayIdState(configManager?.config?.isWalletPayIdEnabled ?: false)) loadData(data) } } @@ -100,10 +107,10 @@ class TapWalletManager { store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection)) return@withContext } + val config = store.state.globalState.configManager?.config ?: return@withContext + store.dispatch(WalletAction.LoadWallet( - data.walletManager.wallet, data.verifyResponse?.artworkInfo?.id, - TapWorkarounds.isStart2Coin != true && TapConfig.useTopUp - )) + data.walletManager.wallet, data.verifyResponse?.artworkInfo?.id, config.isTopUpEnabled)) store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) store.dispatch(WalletAction.LoadFiatRate) store.dispatch(WalletAction.LoadPayId) @@ -135,7 +142,8 @@ class TapWalletManager { val error = result.error val blockchain = walletManager.wallet.blockchain if (error != null && blockchain.isNoAccountError(error)) { - val amountToCreateAccount = blockchain.amountToCreateAccount(walletManager.wallet.token) + val token = walletManager.wallet.getFirstToken() + val amountToCreateAccount = blockchain.amountToCreateAccount(token) if (amountToCreateAccount != null) { store.dispatch(WalletAction.LoadWallet.NoAccount(amountToCreateAccount.toString())) return@withContext @@ -150,8 +158,8 @@ class TapWalletManager { private suspend fun loadPayIdIfNeeded(): Result? { val scanNoteResponse = store.state.globalState.scanNoteResponse - if (!TapConfig.usePayId || - scanNoteResponse?.walletManager?.wallet?.blockchain?.isPayIdSupported() == false) { + if (store.state.globalState.configManager?.config?.isWalletPayIdEnabled == false + || scanNoteResponse?.walletManager?.wallet?.blockchain?.isPayIdSupported() == false) { return null } val cardId = scanNoteResponse?.card?.cardId @@ -166,11 +174,12 @@ class TapWalletManager { withContext(Dispatchers.Main) { when (result) { is Result.Success -> { - val payId = result.data - if (TapWorkarounds.isPayIdEnabled() == false) { + val config = store.state.globalState.configManager?.config + if (config?.isWalletPayIdEnabled == false) { store.dispatch(WalletAction.DisablePayId) return@withContext } + val payId = result.data if (payId == null) { store.dispatch(WalletAction.LoadPayId.NotCreated) } else { @@ -198,4 +207,8 @@ class TapWalletManager { } } } +} + +fun Wallet.getFirstToken(): Token? { + return getTokens().toList().getOrNull(0) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt index de1640fd9c..bb5e44f015 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -12,10 +12,5 @@ object TapWorkarounds { isStart2Coin = card.cardData?.issuerName?.toLowerCase(Locale.US) == START_2_COIN_ISSUER } - fun isPayIdEnabled(): Boolean { - if (isStart2Coin) return false - return true - } - const val START_2_COIN_ISSUER = "start2coin" } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TopUpHelper.kt b/app/src/main/java/com/tangem/tap/domain/TopUpHelper.kt index 7494073c72..5f5ae80096 100644 --- a/app/src/main/java/com/tangem/tap/domain/TopUpHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/TopUpHelper.kt @@ -1,7 +1,6 @@ package com.tangem.tap.domain import android.net.Uri -import com.tangem.tap.TapConfig import com.tangem.tap.common.redux.global.CryptoCurrencyName import org.spongycastle.util.encoders.Base64 import javax.crypto.Mac @@ -19,12 +18,12 @@ class TopUpHelper { // private const val REDIRECT_URL_PATH = "&redirectUrl=" private const val SIGNATURE_PATH = "&signature=" - fun getUrl(cryptoCurrencyName: CryptoCurrencyName, walletAddress: String): String { - val originalQuery = API_KEY_PATH + TapConfig.moonPayApiKey.urlEncode() + + fun getUrl(cryptoCurrencyName: CryptoCurrencyName, walletAddress: String, apiKey: String, secretKey: String): String { + val originalQuery = API_KEY_PATH + apiKey.urlEncode() + CURRENCY_PATH + cryptoCurrencyName.urlEncode() + WALLET_ADDRESS_PATH + walletAddress.urlEncode() // REDIRECT_URL_PATH + REDIRECT_URL.urlEncode() - val signature = createSignature(originalQuery, TapConfig.moonPayApiSecretKey) + val signature = createSignature(originalQuery, secretKey) return BASE_URL + originalQuery + SIGNATURE_PATH + signature.urlEncode() } diff --git a/app/src/main/java/com/tangem/tap/domain/config/ConfigLoader.kt b/app/src/main/java/com/tangem/tap/domain/config/ConfigLoader.kt new file mode 100644 index 0000000000..60bb343e02 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/config/ConfigLoader.kt @@ -0,0 +1,81 @@ +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 +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types +import com.tangem.wallet.BuildConfig +import timber.log.Timber + +/** +[REDACTED_AUTHOR] + */ +interface ConfigLoader { + fun loadConfig(onComplete: (ConfigModel) -> Unit) + + companion object { + const val featuresName = "features_${BuildConfig.CONFIG_ENVIRONMENT}" + const val configValuesName = "config_${BuildConfig.CONFIG_ENVIRONMENT}" + } +} + + +class LocalLoader( + private val context: Context, + private val moshi: Moshi +) : ConfigLoader { + + override fun loadConfig(onComplete: (ConfigModel) -> Unit) { + val config = try { + val featureType = Types.newParameterizedType(List::class.java, FeatureModel::class.java) + val featureAdapter: JsonAdapter> = moshi.adapter(featureType) + val valuesType = Types.newParameterizedType(List::class.java, ConfigValueModel::class.java) + val valuesAdapter: JsonAdapter> = moshi.adapter(valuesType) + + val jsonFeatures = readAssetAsString(ConfigLoader.featuresName) + val jsonConfigValues = readAssetAsString(ConfigLoader.configValuesName) + + ConfigModel(featureAdapter.fromJson(jsonFeatures) ?: listOf(), + valuesAdapter.fromJson(jsonConfigValues) ?: listOf()) + } catch (ex: Exception) { + Timber.e(ex) + ConfigModel.empty() + } + onComplete(config) + } + + private fun readAssetAsString(fileName: String): String { + return context.assets.open("$fileName.json").bufferedReader().readText() + } +} + +class RemoteLoader( + private val moshi: Moshi +) : ConfigLoader { + + override fun loadConfig(onComplete: (ConfigModel) -> Unit) { + val emptyConfig = ConfigModel.empty() + val remoteConfig = Firebase.remoteConfig + remoteConfig.fetchAndActivate().addOnCompleteListener { + if (it.isSuccessful) { + val config = remoteConfig.getValue(ConfigLoader.featuresName) + val jsonConfig = config.asString() + if (jsonConfig.isEmpty()) { + onComplete(emptyConfig) + return@addOnCompleteListener + } + val featureType = Types.newParameterizedType(List::class.java, FeatureModel::class.java) + val featureAdapter: JsonAdapter> = moshi.adapter(featureType) + onComplete(ConfigModel(featureAdapter.fromJson(jsonConfig) ?: listOf(), listOf())) + } else { + onComplete(emptyConfig) + } + }.addOnFailureListener { + FirebaseCrashlytics.getInstance().recordException(it) + onComplete(emptyConfig) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/config/ConfigManager.kt b/app/src/main/java/com/tangem/tap/domain/config/ConfigManager.kt new file mode 100644 index 0000000000..339aeb3124 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/config/ConfigManager.kt @@ -0,0 +1,90 @@ +package com.tangem.tap.domain.config + +import com.tangem.tangem_sdk_new.ui.animation.VoidCallback + +/** +[REDACTED_AUTHOR] + */ +data class Config( + val coinMarketCapKey: String = "f6622117-c043-47a0-8975-9d673ce484de", + val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE", + val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C", + val isWalletPayIdEnabled: Boolean = true, + val isTopUpEnabled: Boolean = false, +) + +class ConfigManager( + private val localLoader: ConfigLoader, + private val remoteLoader: ConfigLoader +) { + + var config: Config = Config() + private set + + private var defaultConfig = Config() + + fun load(onComplete: VoidCallback? = null) { + localLoader.loadConfig { config -> + config.features?.forEach { setupFeature(it.name, it.value) } + config.configValues?.forEach { setupKey(it.name, it.value) } + } + remoteLoader.loadConfig { config -> + config.features?.forEach { setupFeature(it.name, it.value) } + onComplete?.invoke() + } + } + + fun turnOff(name: String) { + when (name) { + isWalletPayIdEnabled -> config = config.copy(isWalletPayIdEnabled = false) + isTopUpEnabled -> config = config.copy(isTopUpEnabled = false) + } + } + + fun resetToDefault(name: String) { + when (name) { + isWalletPayIdEnabled -> config = config.copy(isWalletPayIdEnabled = defaultConfig.isWalletPayIdEnabled) + isTopUpEnabled -> config = config.copy(isTopUpEnabled = defaultConfig.isTopUpEnabled) + } + } + + private fun setupFeature(name: String, value: Boolean) { + val newValue = value ?: return + + when (name) { + isWalletPayIdEnabled -> { + config = config.copy(isWalletPayIdEnabled = newValue) + defaultConfig = defaultConfig.copy(isWalletPayIdEnabled = newValue) + } + isTopUpEnabled -> { + config = config.copy(isTopUpEnabled = newValue) + defaultConfig = defaultConfig.copy(isTopUpEnabled = newValue) + } + } + } + + private fun setupKey(name: String, value: String) { + when (name) { + 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 isWalletPayIdEnabled = "isWalletPayIdEnabled" + const val isTopUpEnabled = "useTopUp" + const val coinMarketCapKey = "coinMarketCapKey" + const val moonPayApiKey = "moonPayApiKey" + const val moonPayApiSecretKey = "moonPayApiSecretKey" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/config/JsonModels.kt b/app/src/main/java/com/tangem/tap/domain/config/JsonModels.kt new file mode 100644 index 0000000000..1b7272765b --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/config/JsonModels.kt @@ -0,0 +1,26 @@ +package com.tangem.tap.domain.config + +/** +[REDACTED_AUTHOR] + */ + +interface BaseConfigModel { + val name: String + val value: V? +} + +class FeatureModel( + override val name: String, + override val value: Boolean +) : BaseConfigModel + +class ConfigValueModel( + override val name: String, + override val value: String +) : BaseConfigModel + +class ConfigModel(val features: List?, val configValues: List?) { + companion object { + fun empty(): ConfigModel = ConfigModel(listOf(), listOf()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index 987e3c1b3b..586051a676 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -25,7 +25,7 @@ class HomeMiddleware { when (action) { is HomeAction.CheckIfFirstLaunch -> { store.dispatch( - HomeAction.CheckIfFirstLaunch.Result(preferencesStorage.isFirstLaunch()) + HomeAction.CheckIfFirstLaunch.Result(preferencesStorage.getCountOfLaunches() == 1) ) } is HomeAction.ReadCard -> { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt index 911a51af79..e54f0f07dc 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt @@ -33,6 +33,7 @@ sealed class AddressPayIdActionUi : SendScreenActionUi { object CheckAddressPayId : AddressPayIdActionUi() data class SetTruncateHandler(val handler: (String) -> String) : AddressPayIdActionUi() data class TruncateOrRestore(val truncate: Boolean) : AddressPayIdActionUi() + data class ChangePayIdState(val walletPayIdEnabled: Boolean): AddressPayIdActionUi() } sealed class AddressPayIdVerifyAction : SendScreenAction { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt index 680654633b..74fdac0d5d 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt @@ -4,7 +4,6 @@ import com.tangem.blockchain.common.Wallet import com.tangem.commands.common.network.Result import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.PayIdManager -import com.tangem.tap.domain.TapWorkarounds import com.tangem.tap.domain.isPayIdSupported import com.tangem.tap.features.send.redux.AddressPayIdActionUi import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction @@ -15,6 +14,7 @@ import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.PayIdVerifica import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.PayIdVerification.SetPayIdWalletAddress import com.tangem.tap.features.send.redux.FeeAction import com.tangem.tap.scope +import com.tangem.tap.store import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -49,7 +49,7 @@ internal class AddressPayIdMiddleware { private fun setAddressAndCheck(data: String, isUserInput: Boolean, dispatch: (Action) -> Unit) { val potentialPayId = data.toLowerCase() - if (PayIdManager.isPayId(potentialPayId) && TapWorkarounds.isPayIdEnabled()) { + if (PayIdManager.isPayId(potentialPayId) && isPayIdEnabled()) { dispatch(SetPayIdWalletAddress(potentialPayId, "", isUserInput)) } else { dispatch(SetWalletAddress(data, isUserInput)) @@ -63,7 +63,7 @@ internal class AddressPayIdMiddleware { val addressPayId = sendState.addressPayIdState.normalFieldValue ?: return val isUserInput = sendState.addressPayIdState.viewFieldValue.isFromUserInput - if (PayIdManager.isPayId(addressPayId) && TapWorkarounds.isPayIdEnabled()) { + if (PayIdManager.isPayId(addressPayId) && isPayIdEnabled()) { verifyPayId(addressPayId, wallet, isUserInput, dispatch) } else { verifyAddress(addressPayId, wallet, isUserInput, dispatch) @@ -152,10 +152,14 @@ internal class AddressPayIdMiddleware { } } - if (PayIdManager.isPayId(addressPayId) && TapWorkarounds.isPayIdEnabled()) { + if (PayIdManager.isPayId(addressPayId) && isPayIdEnabled()) { verifyPayId(addressPayId, wallet, false, internalDispatcher) } else { verifyAddress(addressPayId, wallet, false, internalDispatcher) } } + + private fun isPayIdEnabled(): Boolean { + return store.state.globalState.configManager?.config?.isWalletPayIdEnabled ?: false + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt index e7c726241f..d7a1c97f10 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt @@ -30,6 +30,7 @@ class AddressPayIdReducer : SendInternalReducer { is AddressPayIdActionUi.PasteAddressPayId -> return sendState is AddressPayIdActionUi.CheckClipboard -> return sendState is AddressPayIdActionUi.CheckAddressPayId -> return sendState + is AddressPayIdActionUi.ChangePayIdState -> state.copy(walletPayIdEnabled = action.walletPayIdEnabled) } return updateLastState(sendState.copy(addressPayIdState = result), result) } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt index c6f6511773..1ecf9ea6bb 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt @@ -4,6 +4,7 @@ import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Wallet import com.tangem.tap.common.extensions.scaleToFiat import com.tangem.tap.common.extensions.stripZeroPlainString +import com.tangem.tap.domain.getFirstToken import com.tangem.tap.features.send.redux.ReceiptAction.RefreshReceipt import com.tangem.tap.features.send.redux.SendScreenAction import com.tangem.tap.features.send.redux.states.* @@ -179,7 +180,7 @@ class ReceiptReducer : SendInternalReducer { return ReceiptSymbols( fiat = store.state.globalState.appCurrency, crypto = wallet.blockchain.currency, - token = wallet.amounts[AmountType.Token]?.currencySymbol + token = wallet.getFirstToken()?.symbol ) } @@ -187,12 +188,12 @@ class ReceiptReducer : SendInternalReducer { return when (mainCurrencyType) { MainCurrencyType.FIAT -> when (amountType) { AmountType.Coin -> ReceiptLayoutType.FIAT - AmountType.Token -> ReceiptLayoutType.TOKEN_FIAT + is AmountType.Token -> ReceiptLayoutType.TOKEN_FIAT AmountType.Reserve -> ReceiptLayoutType.UNKNOWN } MainCurrencyType.CRYPTO -> when (amountType) { AmountType.Coin -> ReceiptLayoutType.CRYPTO - AmountType.Token -> ReceiptLayoutType.TOKEN_CRYPTO + is AmountType.Token -> ReceiptLayoutType.TOKEN_CRYPTO AmountType.Reserve -> ReceiptLayoutType.UNKNOWN } } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt index dfcc6ca4c5..b7a1622789 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt @@ -9,6 +9,7 @@ data class AddressPayIdState( val recipientWalletAddress: String? = null, val error: AddressPayIdVerifyAction.Error? = null, val truncateHandler: ((String) -> String)? = null, + val walletPayIdEnabled: Boolean = false, val pasteIsEnabled: Boolean = false ) : SendScreenState { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index 68954619a6..2e3a6e4955 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt @@ -78,14 +78,14 @@ data class SendState( fun convertFiatToExtractCrypto(fiatValue: BigDecimal): BigDecimal = when (amountState.typeOfAmount) { AmountType.Coin -> convertFiatToCoin(fiatValue) - AmountType.Token -> convertFiatToToken(fiatValue) + is AmountType.Token -> convertFiatToToken(fiatValue) AmountType.Reserve -> fiatValue } fun convertExtractCryptoToFiat(cryptoValue: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { return when (amountState.typeOfAmount) { AmountType.Coin -> convertCoinToFiat(cryptoValue, scaleWithPrecision) - AmountType.Token -> convertTokenToFiat(cryptoValue, scaleWithPrecision) + is AmountType.Token -> convertTokenToFiat(cryptoValue, scaleWithPrecision) AmountType.Reserve -> cryptoValue } } @@ -103,7 +103,7 @@ data class SendState( fun mainCurrencyCanBeSwitched(): Boolean { return when (amountState.typeOfAmount) { AmountType.Coin -> coinIsConvertible() - AmountType.Token -> tokenIsConvertible() + is AmountType.Token -> tokenIsConvertible() AmountType.Reserve -> false } } diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index b9d542836f..9714886b7b 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -240,7 +240,7 @@ class FeeUiHelper { companion object { fun feeToId(fee: FeeType): Int { return when (fee) { - FeeType.SINGLE -> 0 + FeeType.SINGLE -> View.NO_ID FeeType.LOW -> R.id.chipLow FeeType.NORMAL -> R.id.chipNormal FeeType.PRIORITY -> R.id.chipPriority diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index bdd2e9f3c4..087c63af7a 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -3,6 +3,7 @@ package com.tangem.tap.features.send.ui.stateSubscribers import android.app.Dialog import android.content.Context import android.text.SpannableStringBuilder +import android.view.View import android.view.ViewGroup import androidx.core.text.bold import com.tangem.tap.common.extensions.* @@ -10,7 +11,6 @@ import com.tangem.tap.common.redux.getMessageString import com.tangem.tap.common.text.DecimalDigitsInputFilter import com.tangem.tap.common.toggleWidget.ProgressState import com.tangem.tap.domain.MultiMessageError -import com.tangem.tap.domain.TapWorkarounds import com.tangem.tap.domain.assembleErrors import com.tangem.tap.features.send.BaseStoreFragment import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error @@ -104,7 +104,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber val til = fg.tilAddressOrPayId val parsedError = parseError(til.context, state.error) - val hintResId = if (TapWorkarounds.isPayIdEnabled()) { + val hintResId = if (state.walletPayIdEnabled) { R.string.send_destination_hint_address_payid }else { R.string.send_destination_hint_address @@ -201,7 +201,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber } val chipId = FeeUiHelper.feeToId(state.selectedFeeType) - if (fg.chipGroup.checkedChipId != chipId && chipId != 0) fg.chipGroup.check(chipId) + if (fg.chipGroup.checkedChipId != chipId && chipId != View.NO_ID) fg.chipGroup.check(chipId) } private fun handleReceiptState(fg: BaseStoreFragment, state: ReceiptState) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index e129bf08b3..4bd5972e2e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -3,6 +3,7 @@ package com.tangem.tap.features.wallet.redux import android.content.Context import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.Wallet +import com.tangem.blockchain.common.address.AddressType import com.tangem.commands.Card import com.tangem.tap.common.redux.ErrorAction import com.tangem.tap.common.redux.NotificationAction @@ -91,4 +92,6 @@ sealed class WalletAction : Action { sealed class TopUpAction : WalletAction() { data class TopUp(val context: Context, val toolbarColor: Int) : TopUpAction() } + + data class ChangeSelectedAddress(val type: AddressType): WalletAction() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt index d1ea1faa17..5aeba06e50 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt @@ -133,13 +133,13 @@ class WalletMiddleware { } } is WalletAction.CopyAddress -> { - store.state.walletState.addressData?.address?.let { + store.state.walletState.walletAddresses?.selectedAddress?.address?.let { action.context.copyToClipboard(it) store.dispatch(WalletAction.CopyAddress.Success) } } is WalletAction.ExploreAddress -> { - val uri = Uri.parse(store.state.walletState.addressData?.exploreUrl) + val uri = Uri.parse(store.state.walletState.walletAddresses?.selectedAddress?.exploreUrl) val intent = Intent(Intent.ACTION_VIEW, uri) ContextCompat.startActivity(action.context, intent, null) } @@ -185,7 +185,7 @@ class WalletMiddleware { private fun prepareSendAction(amount: Amount?): Action { return if (amount != null) { - if (amount.type == AmountType.Token) { + if (amount.type is AmountType.Token) { PrepareSendScreen(store.state.walletState.wallet?.amounts?.get(AmountType.Coin), amount) } else { PrepareSendScreen(amount) @@ -255,9 +255,12 @@ private class TopUpMiddleware { fun handle(action: WalletAction.TopUpAction) { when (action) { is WalletAction.TopUpAction.TopUp -> { + val config = store.state.globalState.configManager?.config ?: return val url = TopUpHelper.getUrl( store.state.walletState.currencyData.currencySymbol!!, - store.state.walletState.addressData!!.address + store.state.walletState.walletAddresses!!.selectedAddress.address, + config.moonPayApiKey, + config.moonPayApiSecretKey ) val customTabsIntent = CustomTabsIntent.Builder() .setToolbarColor(action.toolbarColor) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt index 10fc1610b8..461d3363d0 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt @@ -11,6 +11,7 @@ import com.tangem.tap.common.extensions.toFormattedString import com.tangem.tap.common.extensions.toQrCode import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.getFirstToken import com.tangem.tap.features.wallet.models.removeUnknownTransactions import com.tangem.tap.features.wallet.models.toPendingTransactions import com.tangem.tap.features.wallet.ui.BalanceStatus @@ -43,19 +44,14 @@ private fun internalReduce(action: Action, state: AppState): WalletState { when (action.error) { is TapError.NoInternetConnection -> { val wallet = state.globalState.scanNoteResponse?.walletManager?.wallet - val addressData = if (wallet == null) { - null - } else { - AddressData(wallet.address, wallet.shareUrl, wallet.exploreUrl) - } newState = newState.copy( state = ProgressState.Error, error = ErrorType.NoInternetConnection, - addressData = addressData, + walletAddresses = createAddressList(wallet, newState.walletAddresses), currencyData = BalanceWidgetData( status = BalanceStatus.Unreachable, currency = wallet?.blockchain?.fullName, - token = wallet?.token?.symbol?.let { + token = wallet?.getFirstToken()?.symbol?.let { TokenData("", tokenSymbol = it) }), mainButton = WalletMainButton.SendButton(false), @@ -86,11 +82,11 @@ private fun internalReduce(action: Action, state: AppState): WalletState { BalanceStatus.Loading, wallet.blockchain.fullName, currencySymbol = wallet.blockchain.currency, - token = wallet.token?.symbol?.let { + token = wallet.getFirstToken()?.symbol?.let { TokenData("", tokenSymbol = it) } ), - addressData = AddressData(wallet.address, wallet.shareUrl, wallet.exploreUrl), + walletAddresses = createAddressList(wallet, newState.walletAddresses), mainButton = WalletMainButton.SendButton(false), topUpState = TopUpState(allowed = action.allowTopUp) ) @@ -143,9 +139,9 @@ private fun internalReduce(action: Action, state: AppState): WalletState { } else { newState.currencyData.fiatAmount } - val tokenFiatAmount = if (currency == newState.wallet?.token?.symbol) { - newState.wallet?.amounts?.get(AmountType.Token)?.value - ?.toFiatString(rate, state.globalState.appCurrency) + val token = newState.wallet?.getFirstToken() + val tokenFiatAmount = if (currency == token?.symbol) { + newState.wallet?.getTokenAmount(token)?.value?.toFiatString(rate, state.globalState.appCurrency) } else { newState.currencyData.token?.fiatAmount } @@ -171,8 +167,8 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.ShowQrCode -> { newState = newState.copy( walletDialog = WalletDialog.QrDialog( - newState.addressData?.shareUrl?.toQrCode(), - newState.addressData?.shareUrl, + newState.walletAddresses?.selectedAddress?.shareUrl?.toQrCode(), + newState.walletAddresses?.selectedAddress?.shareUrl, newState.currencyData.currency ) ) @@ -216,10 +212,39 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.TopUpAction -> { newState = newState.copy(topUpState = handleTopUpActions(action, newState.topUpState)) } + is WalletAction.ChangeSelectedAddress -> { + val walletAddresses = newState.walletAddresses ?: return newState + val address = walletAddresses.list.firstOrNull { it.type == action.type } ?: return newState + + newState = newState.copy(walletAddresses = WalletAddresses(address, walletAddresses.list)) + } } return newState } +fun createAddressList(wallet: Wallet?, walletAddresses: WalletAddresses? = null): WalletAddresses? { + if (wallet == null) return null + + val listOfAddressData = mutableListOf() + // put a defaultAddress at the first place + wallet.addresses.forEach { + val addressData = AddressData(it.value, it.type, wallet.getShareUri(it.value), wallet.getExploreUrl(it.value)) + if (it.type == wallet.blockchain.defaultAddressType()) { + listOfAddressData.add(0, addressData) + } else { + listOfAddressData.add(addressData) + } + } + + // restore a selected wallet address + var indexOfSelectedWallet = 0 + walletAddresses?.let { + val index = listOfAddressData.indexOfFirst { it.address == walletAddresses.selectedAddress.address } + if (index != -1) indexOfSelectedWallet = index + } + return WalletAddresses(listOfAddressData[indexOfSelectedWallet], listOfAddressData) +} + private fun handleTopUpActions(action: WalletAction.TopUpAction, state: TopUpState): TopUpState { return when (action) { is WalletAction.TopUpAction.TopUp -> state @@ -230,13 +255,17 @@ private fun onWalletLoaded( wallet: Wallet, walletState: WalletState, topUpAllowed: Boolean? = null ): WalletState { val fiatCurrencySymbol = store.state.globalState.appCurrency - val token = wallet.amounts[AmountType.Token] + val token = wallet.getFirstToken() val tokenData = if (token != null) { - val tokenFiatRate = store.state.globalState.conversionRates.getRate(token.currencySymbol) - val tokenFiatAmount = tokenFiatRate?.let { token.value?.toFiatString(it, fiatCurrencySymbol) } - TokenData( - token.value?.toFormattedString(token.decimals) ?: "", - token.currencySymbol, tokenFiatAmount) + val tokenAmount = wallet.getTokenAmount(token) + if (tokenAmount != null) { + val tokenFiatRate = store.state.globalState.conversionRates.getRate(tokenAmount.currencySymbol) + val tokenFiatAmount = tokenFiatRate?.let { tokenAmount.value?.toFiatString(it, fiatCurrencySymbol) } + TokenData(tokenAmount.value?.toFormattedString(tokenAmount.decimals) ?: "", + tokenAmount.currencySymbol, tokenFiatAmount) + } else { + null + } } else { null } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index d6c916d79e..b55c64bbd4 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -2,7 +2,9 @@ package com.tangem.tap.features.wallet.redux import android.graphics.Bitmap import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Wallet +import com.tangem.blockchain.common.address.AddressType import com.tangem.tap.common.entities.Button import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.features.wallet.models.PendingTransaction @@ -16,7 +18,7 @@ data class WalletState( val wallet: Wallet? = null, val pendingTransactions: List = emptyList(), val hashesCountVerified: Boolean? = null, - val addressData: AddressData? = null, + val walletAddresses: WalletAddresses? = null, val currencyData: BalanceWidgetData = BalanceWidgetData(), val payIdData: PayIdData = PayIdData(), val walletDialog: WalletDialog? = null, @@ -27,6 +29,12 @@ data class WalletState( val showDetails: Boolean = currencyData.status != com.tangem.tap.features.wallet.ui.BalanceStatus.EmptyCard && currencyData.status != com.tangem.tap.features.wallet.ui.BalanceStatus.UnknownBlockchain + + val showSegwitAddress: Boolean + get() { + val listOfAddresses = walletAddresses?.list ?: return false + return wallet?.blockchain == Blockchain.Bitcoin && listOfAddresses.size > 1 + } } sealed class WalletDialog { @@ -60,8 +68,14 @@ sealed class WalletMainButton(enabled: Boolean) : Button(enabled) { class CreateWalletButton(enabled: Boolean) : WalletMainButton(enabled) } +data class WalletAddresses( + val selectedAddress: AddressData, + val list: List +) + data class AddressData( val address: String, + val type: AddressType, val shareUrl: String, val exploreUrl: String ) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index 2be04ad3c9..7575838e78 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -2,11 +2,8 @@ package com.tangem.tap.features.wallet.ui import android.app.Dialog import android.os.Bundle -import android.view.Menu +import android.view.* import android.view.Menu.NONE -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment @@ -14,6 +11,9 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.transition.TransitionInflater import com.google.android.material.snackbar.Snackbar import com.squareup.picasso.Picasso +import com.tangem.blockchain.blockchains.bitcoin.BitcoinAddressType +import com.tangem.blockchain.common.address.AddressType +import com.tangem.tap.common.extensions.beginDelayedTransition import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.common.redux.navigation.AppScreen @@ -29,6 +29,7 @@ import com.tangem.wallet.R import kotlinx.android.synthetic.main.card_balance.* import kotlinx.android.synthetic.main.fragment_wallet.* import kotlinx.android.synthetic.main.layout_address.* +import kotlinx.android.synthetic.main.layout_send_fee.* import kotlinx.android.synthetic.main.layout_wallet_long_buttons.* import kotlinx.android.synthetic.main.layout_wallet_short_buttons.* import org.rekotlin.StoreSubscriber @@ -206,9 +207,30 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber + if (checkedId == -1) return@setOnCheckedChangeListener + + SegwitUiHelper.idToType(checkedId)?.let { + store.dispatch(WalletAction.ChangeSelectedAddress(it)) + } + } + } else { + tv_address.setPadding(tv_address.paddingStart, tvAddressPaddingTop, + tv_address.paddingEnd, tv_address.paddingBottom) + chip_group_segwit.hide() + } + tv_address.text = state.walletAddresses.selectedAddress.address tv_explore?.setOnClickListener { store.dispatch(WalletAction.ExploreAddress(requireContext())) } @@ -285,4 +307,24 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber R.id.chip_legacy + is BitcoinAddressType.Segwit -> R.id.chip_default + else -> View.NO_ID + } + } + + fun idToType(id: Int): AddressType? { + return when (id) { + R.id.chip_default -> BitcoinAddressType.Segwit + R.id.chip_legacy -> BitcoinAddressType.Legacy + else -> null + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt index 47889ff06d..5d78846280 100644 --- a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt +++ b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt @@ -1,6 +1,5 @@ package com.tangem.tap.network.coinmarketcap -import com.tangem.tap.TapConfig import com.tangem.tap.network.createRetrofitInstance import okhttp3.Interceptor import okhttp3.Response @@ -23,20 +22,20 @@ interface CoinMarketCapApi { companion object { private const val baseUrl = "https://pro-api.coinmarketcap.com/" - fun create(): CoinMarketCapApi { + fun create(apiKey: String): CoinMarketCapApi { return createRetrofitInstance( baseUrl, - listOf(createCoinMarketRequestInterceptor()), + listOf(createCoinMarketRequestInterceptor(apiKey)), ).create(CoinMarketCapApi::class.java) } } } -private fun createCoinMarketRequestInterceptor(): Interceptor { +private fun createCoinMarketRequestInterceptor(apiKey: String): Interceptor { return object : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val requestBuilder = chain.request().newBuilder() - requestBuilder.addHeader("X-CMC_PRO_API_KEY", TapConfig.coinMarketCapKey) + requestBuilder.addHeader("X-CMC_PRO_API_KEY", apiKey) return chain.proceed(requestBuilder.build()) } } diff --git a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt index 4dc7931623..fffc439820 100644 --- a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt +++ b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt @@ -3,12 +3,17 @@ package com.tangem.tap.network.coinmarketcap import com.tangem.commands.common.network.Result import com.tangem.commands.common.network.performRequest import com.tangem.tap.common.redux.global.FiatCurrencyName +import com.tangem.tap.store import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.math.BigDecimal -class CoinMarketCapService { - private val api: CoinMarketCapApi by lazy { CoinMarketCapApi.create() } +class CoinMarketCapService() { + private val api: CoinMarketCapApi by lazy { CoinMarketCapApi.create(getApiKey()) } + + private fun getApiKey(): String { + return store.state.globalState.configManager?.config?.coinMarketCapKey ?: "" + } suspend fun getRate( currency: String, fiatCurrency: FiatCurrencyName? = null diff --git a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt index 47f832dbac..6ce4249585 100644 --- a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt +++ b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt @@ -3,6 +3,7 @@ package com.tangem.tap.persistence import android.app.Application import android.content.Context import android.content.SharedPreferences +import androidx.core.content.edit import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.squareup.moshi.Types @@ -18,6 +19,10 @@ class PreferencesStorage(applicationContext: Application) { applicationContext.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) } + init { + incrementLaunchCounter() + } + private val fiatCurrenciesAdapter: JsonAdapter> by lazy { val moshi = Moshi.Builder() .add(KotlinJsonAdapterFactory()) @@ -45,11 +50,7 @@ class PreferencesStorage(applicationContext: Application) { return preferences.edit().putString(FIAT_CURRENCIES_KEY, json).apply() } - fun isFirstLaunch(): Boolean { - val isFirst = !preferences.contains(FIRST_LAUNCH_CHECK_KEY) - if (isFirst) preferences.edit().putInt(FIRST_LAUNCH_CHECK_KEY, System.currentTimeMillis().toInt()).apply() - return isFirst - } + fun getCountOfLaunches(): Int = preferences.getInt(APP_LAUNCH_COUNT_KEY, 1) fun saveScannedCardId(cardId: String) { val scannedCardsIds: String = restoreScannedCardIds() @@ -65,7 +66,6 @@ class PreferencesStorage(applicationContext: Application) { private fun restoreScannedCardIds(): String = preferences.getString(SCANNED_CARDS_IDS_KEY, "") ?: "" - fun saveDisclaimerAccepted() { preferences.edit().putBoolean(DISCLAIMER_ACCEPTED_KEY, true).apply() } @@ -74,6 +74,11 @@ class PreferencesStorage(applicationContext: Application) { return preferences.getBoolean(DISCLAIMER_ACCEPTED_KEY, false) } + private fun incrementLaunchCounter() { + var count = preferences.getInt(APP_LAUNCH_COUNT_KEY, 0) + preferences.edit { putInt(APP_LAUNCH_COUNT_KEY, ++count) } + } + companion object { private const val PREFERENCES_NAME = "tapPrefs" private const val APP_CURRENCY_KEY = "appCurrency" @@ -81,6 +86,7 @@ class PreferencesStorage(applicationContext: Application) { private const val FIRST_LAUNCH_CHECK_KEY = "firstLaunchCheck" private const val SCANNED_CARDS_IDS_KEY = "scannedCardIds" private const val DISCLAIMER_ACCEPTED_KEY = "disclaimerAccepted" + private const val APP_LAUNCH_COUNT_KEY = "launchCount" } } \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/ic_payid.png b/app/src/main/res/drawable-hdpi/ic_payid.png deleted file mode 100644 index 79c7f1ec4f..0000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_payid.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_payid.png b/app/src/main/res/drawable-mdpi/ic_payid.png deleted file mode 100644 index 1af851efc8..0000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_payid.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_payid.png b/app/src/main/res/drawable-xhdpi/ic_payid.png deleted file mode 100644 index 914e7f67b9..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_payid.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_payid.png b/app/src/main/res/drawable-xxhdpi/ic_payid.png deleted file mode 100644 index f7dbee2905..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_payid.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_payid.png b/app/src/main/res/drawable-xxxhdpi/ic_payid.png deleted file mode 100644 index bb99030d50..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_payid.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_payid.xml b/app/src/main/res/drawable/ic_payid.xml new file mode 100644 index 0000000000..822e97afc9 --- /dev/null +++ b/app/src/main/res/drawable/ic_payid.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/layout_address.xml b/app/src/main/res/layout/layout_address.xml index ab3ff4955a..699cd908ed 100644 --- a/app/src/main/res/layout/layout_address.xml +++ b/app/src/main/res/layout/layout_address.xml @@ -23,21 +23,52 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> + + + + + + + + + app:layout_constraintTop_toBottomOf="@id/chip_group_segwit"> + app:layout_constraintTop_toBottomOf="@id/chip_group_segwit"> + tools:text="roman$payid.tangem.com" /> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d2b3bb460e..ae4aa7218f 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -40,18 +40,18 @@ Erhalten\u0020 von %s Adresse erkunden - PayID erstellen + PayString erstellen %s Wallet - PayID erstellen + PayString erstellen Karte: %s - PayID Name + PayString Name $payid.tangem.com Erstellen - PayID sind Ihre einzigartigen Identifikationsdaten wie Telefonnummer, E-Mail-Adresse oder ABN. - Ihre PayID wurde erfolgreich erstellt und zum Clipboard kopiert - Fehlerantwort beim Erstellen der ZahlungsID - Diese PayID existiert bereits. Versuchen Sie mit einer anderen erneut. - Adresse oder PayID + PayString sind Ihre einzigartigen Identifikationsdaten wie Telefonnummer, E-Mail-Adresse oder ABN. + Ihre PayString wurde erfolgreich erstellt und zum Clipboard kopiert + Fehlerantwort beim Erstellen der PayString + Diese PayString existiert bereits. Versuchen Sie mit einer anderen erneut. + Adresse oder PayString Adresse Absenden Netzgebühr @@ -105,7 +105,7 @@ Tippen Sie um ein Wallet zu erstellen Tippen Sie um den Zugangscode zu ändern Tippen Sie um den Passcode zu ändern - Ungültige ZahlungsID + Ungültige PayString @@ -167,10 +167,10 @@ RECHTLICHER HAFTUNGSAUSSCHLUSS Erhalt der Gebühr fehlgeschlagen Unbekannter Fehler - Verifikation der PayID ist fehlgeschlagen - PayID wird von der Blockchain nicht unterstützt - PayID ist nicht registriert - PayID-Anfrage ist fehlgeschlagen + Verifikation der PayString ist fehlgeschlagen + PayString wird von der Blockchain nicht unterstützt + PayString ist nicht registriert + PayString-Anfrage ist fehlgeschlagen Die Adresse stimmt mit der Adresse Ihrer Brieftasche überein Nicht genug Geld @@ -182,7 +182,7 @@ RECHTLICHER HAFTUNGSAUSSCHLUSS Die Adresse wurde erfolgreich kopiert - Zuerst geben Sie die gewünschte PayID ein + Zuerst geben Sie die gewünschte PayString ein Legen Sie die Karte zum Scannen an Legen Sie die Karte an diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6c2934f23c..043edee6a3 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -41,18 +41,18 @@ Réception \u0020 de %s Explorer l\'adresse - Créer PayID + Créer PayString Portefeuille %s - Créer PayID + Créer PayString Carte: %s - Dénomination PayID + Dénomination PayString $payid.tangem.com Créer - Votre PayID est une information qui vous est propre, comme un numéro de téléphone, une adresse du courrier électronique ou un ABN. - PayID a été créé avec succès et copié dans le presse-papiers - Mauvaise réponse lors de la création de PayID - Ce PayID existe déjà. Essayez une autre variante. - Adresse ou PayID + Votre PayString est une information qui vous est propre, comme un numéro de téléphone, une adresse du courrier électronique ou un ABN. + PayString a été créé avec succès et copié dans le presse-papiers + Mauvaise réponse lors de la création de PayString + Ce PayString existe déjà. Essayez une autre variante. + Adresse ou PayString Adresse Envoyer Commissions du réseau @@ -105,7 +105,7 @@ Touchez, pour créer un portefeuille Touchez, pour modifier le code d’accès Touchez, pour modifier le mot de passe - PayID incorrect + PayString incorrect @@ -165,10 +165,10 @@ Avertissement Échec de réception des commissions Erreur inconnue - La vérification de PayID a échoué - PayID non pris en charge par la blockchain - PayID non enregistré - La demande de PayID a échoué + La vérification de PayString a échoué + PayString non pris en charge par la blockchain + PayString non enregistré + La demande de PayString a échoué L’adresse est la même que celle de votre portefeuille Solde insuffisant Erreur interne de la blockchain @@ -176,7 +176,7 @@ Avertissement Le montant minimal est de %ы Le reste est trop petit L’adresse a été copiée avec succès - Saisissez d’abord le PayID requis + Saisissez d’abord le PayString requis Posez pour scanner Posez la carte Votre solde sur ce portefeuille n\'est pas nul, ou vous avez des transactions non confirmées. Impossible de supprimer la fonction du portefeuille diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 909b654b6b..08315d8822 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -39,18 +39,18 @@ Ricevi\u0020 da %s Cerca indirizzo - Crea PayID + Crea PayString portafoglio %s - Crea PayID + Crea PayString Scheda: %s - Nome PayID + Nome PayString $payid.tangem.com Crea - Il tuo PayID – informazioni per te uniche, come il tuo numero di telefono, indirizzo email o ABN. - PayID creato con successo e copiato negli appunti - Errore di risposta durante la creazione del PayID - Questo PayID esiste già. Scegline un altro. - Indirizzo o PayID + Il tuo PayString – informazioni per te uniche, come il tuo numero di telefono, indirizzo email o ABN. + PayString creato con successo e copiato negli appunti + Errore di risposta durante la creazione del PayString + Questo PayString esiste già. Scegline un altro. + Indirizzo o PayString Indirizzo Invia Rete libera @@ -102,7 +102,7 @@ Avvicina per creare il portafoglio Avvicina per modificare il codice di accesso Avvicina per modificare la password - PayID non valido + PayString non valido @@ -166,10 +166,10 @@ Questa nota legale è stata modificata l\'ultima volta 01.10.2020. Nessuna connessione a Internet Errore sconosciuto - Verifica del PayID fallita - PayID non supportato dalla blockchain - PayID non registrato - Richiesta PayID fallita + Verifica del PayString fallita + PayString non supportato dalla blockchain + PayString non registrato + Richiesta PayString fallita L\'indirizzo corrisponde all\'indirizzo del tuo portafoglio Saldo insufficiente @@ -181,7 +181,7 @@ Questa nota legale è stata modificata l\'ultima volta 01.10.2020. L\'indirizzo è stato copiato con successo - Inserire prima il PayID desiderato + Inserire prima il PayString desiderato Avvicina per scansionare Avvicina la scheda diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 49798d893b..fd2c18a380 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -33,5 +33,6 @@ 44dp 32sp + 16dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 80f2e89d5f..574aa2d17c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -49,20 +49,20 @@ Sending\u0020 Explore address - Create PayID + Create PayString %s wallet - Create PayID + Create PayString Card: %s - PayID name + PayString name $payid.tangem.com Create - Your PayID is information unique to you, like your phone number, email or ABN. - PayID created successfully and copied to clipboard - Error response while creating PayID - This PayID already exists. Try a different one. + Your PayString is information unique to you, like your phone number, email or ABN. + PayString created successfully and copied to clipboard + Error response while creating PayString + This PayString already exists. Try a different one. - Address or PayID + Address or PayString Address Send Network fee @@ -119,7 +119,7 @@ Tap to change the access code Tap to change the passcode - Invalid PayID + Invalid PayString Legal Disclaimer \n @@ -177,10 +177,10 @@ No internet connection Unknown error - PayID verification failed - PayID unsupported by blockchain - PayID not registered - PayID request failed + PayString verification failed + PayString unsupported by blockchain + PayString not registered + PayString request failed Address is the same as wallet address Insufficient balance @@ -192,7 +192,7 @@ Address was copied to clipboard - Enter desired PayID first + Enter desired PayString first Tap to scan Tap the card diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 834037ecad..fa4aa42ee7 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -1,3 +1,6 @@ Top Up + Default + Compatibility + Legacy diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 5c6ee06378..ae260a7854 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -51,7 +51,7 @@ @color/selector_btn_black -