Updated on 2026-08-14
This commit is contained in:
parent
054b7c88d3
commit
434a8ac74f
26 changed files with 506 additions and 207 deletions
|
|
@ -82,6 +82,9 @@ dependencies {
|
|||
}
|
||||
}
|
||||
|
||||
// WebView
|
||||
implementation "androidx.browser:browser:1.2.0"
|
||||
|
||||
//lifecycle
|
||||
implementation "androidx.lifecycle:lifecycle-runtime:2.2.0"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
|
||||
|
|
|
|||
|
|
@ -3,4 +3,6 @@ package com.tangem.tap
|
|||
object TapConfig {
|
||||
const val usePayId: Boolean = true
|
||||
const val coinMarketCapKey = "f6622117-c043-47a0-8975-9d673ce484de"
|
||||
const val moonPayApiKey = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE"
|
||||
const val moonPayApiSecretKey = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C"
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment
|
|||
import com.tangem.tap.features.home.HomeFragment
|
||||
import com.tangem.tap.features.send.ui.SendFragment
|
||||
import com.tangem.tap.features.wallet.ui.WalletFragment
|
||||
import com.tangem.tap.features.wallet.ui.topup.TopUpFragment
|
||||
import com.tangem.wallet.R
|
||||
|
||||
fun FragmentActivity.openFragment(screen: AppScreen, addToBackStack: Boolean = true) {
|
||||
|
|
@ -39,6 +40,7 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
|
|||
return when (screen) {
|
||||
AppScreen.Home -> HomeFragment()
|
||||
AppScreen.Wallet -> WalletFragment()
|
||||
AppScreen.TopUp -> TopUpFragment()
|
||||
AppScreen.Send -> SendFragment()
|
||||
AppScreen.Details -> DetailsFragment()
|
||||
AppScreen.DetailsConfirm -> DetailsConfirmFragment()
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import com.tangem.tap.features.home.redux.HomeMiddleware
|
|||
import com.tangem.tap.features.home.redux.HomeState
|
||||
import com.tangem.tap.features.send.redux.middlewares.sendMiddleware
|
||||
import com.tangem.tap.features.send.redux.states.SendState
|
||||
import com.tangem.tap.features.wallet.redux.WalletMiddleware
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.walletMiddleware
|
||||
import org.rekotlin.Middleware
|
||||
import org.rekotlin.StateType
|
||||
|
||||
|
|
@ -31,7 +31,9 @@ data class AppState(
|
|||
fun getMiddleware(): List<Middleware<AppState>> {
|
||||
return listOf(
|
||||
logMiddleware, navigationMiddleware, notificationsMiddleware, globalMiddleware,
|
||||
HomeMiddleware().homeMiddleware, walletMiddleware, sendMiddleware,
|
||||
HomeMiddleware().homeMiddleware,
|
||||
WalletMiddleware().walletMiddleware,
|
||||
sendMiddleware,
|
||||
DetailsMiddleware().detailsMiddleware,
|
||||
DisclaimerMiddleware().disclaimerMiddleware
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,4 +9,4 @@ data class NavigationState(
|
|||
val activity: WeakReference<FragmentActivity>? = null
|
||||
) : StateType
|
||||
|
||||
enum class AppScreen { Home, Wallet, Send, Details, DetailsConfirm, DetailsSecurity, Disclaimer }
|
||||
enum class AppScreen { Home, Wallet, TopUp, Send, Details, DetailsConfirm, DetailsSecurity, Disclaimer }
|
||||
|
|
@ -56,7 +56,10 @@ class TapWalletManager {
|
|||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is Result.Success -> store.dispatch(WalletAction.UpdateWallet.Success(result.data))
|
||||
is Result.Success ->
|
||||
store.dispatch(WalletAction.UpdateWallet.Success(
|
||||
result.data, tapWorkarounds?.isStart2Coin != true
|
||||
))
|
||||
is Result.Failure ->
|
||||
store.dispatch(WalletAction.UpdateWallet.Failure(result.error?.localizedMessage))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import com.tangem.commands.Card
|
|||
|
||||
class TapWorkarounds(val card: Card) {
|
||||
|
||||
private val isStart2Coin: Boolean = card.cardData?.issuerName == START_2_COIN_ISSUER
|
||||
val isStart2Coin: Boolean = card.cardData?.issuerName == START_2_COIN_ISSUER
|
||||
|
||||
fun isPayIdCreationEnabled(): Boolean {
|
||||
if (isStart2Coin) return false
|
||||
|
|
|
|||
44
app/src/main/java/com/tangem/tap/domain/TopUpHelper.kt
Normal file
44
app/src/main/java/com/tangem/tap/domain/TopUpHelper.kt
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
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
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class TopUpHelper {
|
||||
|
||||
companion object {
|
||||
const val REDIRECT_URL = "https://success.tangem.com"
|
||||
|
||||
private const val BASE_URL = "https://buy-staging.moonpay.io"
|
||||
private const val API_KEY_PATH = "?apiKey="
|
||||
private const val CURRENCY_PATH = "¤cyCode="
|
||||
private const val WALLET_ADDRESS_PATH = "&walletAddress="
|
||||
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() +
|
||||
CURRENCY_PATH + cryptoCurrencyName.urlEncode() +
|
||||
WALLET_ADDRESS_PATH + walletAddress.urlEncode() +
|
||||
REDIRECT_URL_PATH + REDIRECT_URL.urlEncode()
|
||||
val signature = createSignature(originalQuery, TapConfig.moonPayApiSecretKey)
|
||||
|
||||
return BASE_URL + originalQuery + SIGNATURE_PATH + signature.urlEncode()
|
||||
}
|
||||
|
||||
private fun String.urlEncode(): String {
|
||||
return Uri.encode(this)
|
||||
}
|
||||
|
||||
|
||||
private fun createSignature(data: String, key: String): String {
|
||||
val sha256Hmac = Mac.getInstance("HmacSHA256")
|
||||
val secretKey = SecretKeySpec(key.toByteArray(), "HmacSHA256")
|
||||
sha256Hmac.init(secretKey)
|
||||
return Base64.toBase64String(sha256Hmac.doFinal(data.toByteArray()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@ sealed class WalletAction : Action {
|
|||
|
||||
object UpdateWallet : WalletAction() {
|
||||
object ScheduleUpdatingWallet : WalletAction()
|
||||
data class Success(val wallet: Wallet) : WalletAction()
|
||||
data class Success(val wallet: Wallet, val topUpAllowed: Boolean) : WalletAction()
|
||||
data class Failure(val errorMessage: String? = null) : WalletAction()
|
||||
}
|
||||
|
||||
|
|
@ -85,4 +85,10 @@ sealed class WalletAction : Action {
|
|||
data class ExploreAddress(val context: Context) : WalletAction()
|
||||
object CreateWallet : WalletAction()
|
||||
object EmptyWallet : WalletAction()
|
||||
|
||||
sealed class TopUpAction : WalletAction() {
|
||||
data class TopUp(val context: Context, val toolbarColor: Int?) : TopUpAction()
|
||||
data class Start(val url: String, val redirectUrl: String) : TopUpAction()
|
||||
data class Finish(val topUpCompleted: Boolean) : TopUpAction()
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import com.tangem.tap.common.redux.navigation.AppScreen
|
|||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.PayIdManager
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.TopUpHelper
|
||||
import com.tangem.tap.domain.extensions.toSendableAmounts
|
||||
import com.tangem.tap.features.send.redux.PrepareSendScreen
|
||||
import com.tangem.tap.features.wallet.models.toPendingTransactions
|
||||
|
|
@ -29,211 +30,239 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.withContext
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
|
||||
class WalletMiddleware {
|
||||
private val topUpMiddleware = TopUpMiddleware()
|
||||
|
||||
val walletMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
is WalletAction.LoadWallet -> {
|
||||
scope.launch {
|
||||
store.state.globalState.tapWalletManager.loadWalletData()
|
||||
}
|
||||
}
|
||||
is WalletAction.LoadPayId -> {
|
||||
scope.launch {
|
||||
store.state.globalState.tapWalletManager.loadPayId()
|
||||
}
|
||||
}
|
||||
is WalletAction.LoadFiatRate -> {
|
||||
scope.launch {
|
||||
store.state.globalState.tapWalletManager.loadFiatRate(store.state.globalState.appCurrency)
|
||||
}
|
||||
}
|
||||
is WalletAction.CreateWallet -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.createWallet(
|
||||
store.state.globalState.scanNoteResponse?.card?.cardId
|
||||
)
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
store.state.globalState.tapWalletManager.onCardScanned(result.data)
|
||||
}
|
||||
|
||||
val walletMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
is WalletAction.TopUpAction -> topUpMiddleware.handle(action)
|
||||
is WalletAction.LoadWallet -> {
|
||||
scope.launch {
|
||||
store.state.globalState.tapWalletManager.loadWalletData()
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.UpdateWallet -> {
|
||||
scope.launch { store.state.globalState.tapWalletManager.updateWallet() }
|
||||
}
|
||||
is WalletAction.UpdateWallet.Success -> setupWalletUpdate(action.wallet)
|
||||
is WalletAction.LoadWallet.Success -> {
|
||||
store.dispatch(WalletAction.CheckHashesCountOnline)
|
||||
if (!store.state.walletState.updatingWallet) setupWalletUpdate(action.wallet)
|
||||
}
|
||||
is WalletAction.CreatePayId.CompleteCreatingPayId -> {
|
||||
scope.launch {
|
||||
val cardId = store.state.globalState.scanNoteResponse?.card?.cardId
|
||||
val wallet = store.state.globalState.scanNoteResponse?.walletManager?.wallet
|
||||
val publicKey = store.state.globalState.scanNoteResponse?.card?.cardPublicKey
|
||||
if (cardId != null && wallet != null && publicKey != null) {
|
||||
val result = PayIdManager().setPayId(
|
||||
cardId, publicKey.toHexString(),
|
||||
action.payId, wallet.address, wallet.blockchain
|
||||
is WalletAction.LoadPayId -> {
|
||||
scope.launch {
|
||||
store.state.globalState.tapWalletManager.loadPayId()
|
||||
}
|
||||
}
|
||||
is WalletAction.LoadFiatRate -> {
|
||||
scope.launch {
|
||||
store.state.globalState.tapWalletManager.loadFiatRate(store.state.globalState.appCurrency)
|
||||
}
|
||||
}
|
||||
is WalletAction.CreateWallet -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.createWallet(
|
||||
store.state.globalState.scanNoteResponse?.card?.cardId
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is Result.Success ->
|
||||
store.dispatch(WalletAction.CreatePayId.Success(action.payId))
|
||||
is Result.Failure -> {
|
||||
val error = result.error as? TapError
|
||||
?: TapError.PayIdCreatingError
|
||||
store.dispatch(WalletAction.CreatePayId.Failure(error))
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
store.state.globalState.tapWalletManager.onCardScanned(result.data)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.UpdateWallet -> {
|
||||
scope.launch { store.state.globalState.tapWalletManager.updateWallet() }
|
||||
}
|
||||
is WalletAction.UpdateWallet.Success -> setupWalletUpdate(action.wallet)
|
||||
is WalletAction.LoadWallet.Success -> {
|
||||
store.dispatch(WalletAction.CheckHashesCountOnline)
|
||||
if (!store.state.walletState.updatingWallet) setupWalletUpdate(action.wallet)
|
||||
}
|
||||
is WalletAction.CreatePayId.CompleteCreatingPayId -> {
|
||||
scope.launch {
|
||||
val cardId = store.state.globalState.scanNoteResponse?.card?.cardId
|
||||
val wallet = store.state.globalState.scanNoteResponse?.walletManager?.wallet
|
||||
val publicKey = store.state.globalState.scanNoteResponse?.card?.cardPublicKey
|
||||
if (cardId != null && wallet != null && publicKey != null) {
|
||||
val result = PayIdManager().setPayId(
|
||||
cardId, publicKey.toHexString(),
|
||||
action.payId, wallet.address, wallet.blockchain
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is Result.Success ->
|
||||
store.dispatch(WalletAction.CreatePayId.Success(action.payId))
|
||||
is Result.Failure -> {
|
||||
val error = result.error as? TapError
|
||||
?: TapError.PayIdCreatingError
|
||||
store.dispatch(WalletAction.CreatePayId.Failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.Scan -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.scanNote(analytics)
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
store.state.globalState.tapWalletManager
|
||||
.onCardScanned(result.data, true)
|
||||
is WalletAction.Scan -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.scanNote(analytics)
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
store.state.globalState.tapWalletManager
|
||||
.onCardScanned(result.data, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.LoadData -> {
|
||||
scope.launch {
|
||||
store.state.globalState.scanNoteResponse?.let {
|
||||
store.state.globalState.tapWalletManager.loadData(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
is NetworkStateChanged -> {
|
||||
store.state.globalState.scanNoteResponse?.let { scanNoteResponse ->
|
||||
store.dispatch(WalletAction.CheckHashesCountOnline)
|
||||
is WalletAction.LoadData -> {
|
||||
scope.launch {
|
||||
store.state.globalState.tapWalletManager.onCardScanned(scanNoteResponse)
|
||||
store.state.globalState.scanNoteResponse?.let {
|
||||
store.state.globalState.tapWalletManager.loadData(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.CopyAddress -> {
|
||||
store.state.walletState.addressData?.address?.let {
|
||||
action.context.copyToClipboard(it)
|
||||
store.dispatch(WalletAction.CopyAddress.Success)
|
||||
is NetworkStateChanged -> {
|
||||
store.state.globalState.scanNoteResponse?.let { scanNoteResponse ->
|
||||
store.dispatch(WalletAction.CheckHashesCountOnline)
|
||||
scope.launch {
|
||||
store.state.globalState.tapWalletManager.onCardScanned(scanNoteResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.CopyAddress -> {
|
||||
store.state.walletState.addressData?.address?.let {
|
||||
action.context.copyToClipboard(it)
|
||||
store.dispatch(WalletAction.CopyAddress.Success)
|
||||
}
|
||||
}
|
||||
is WalletAction.ExploreAddress -> {
|
||||
val uri = Uri.parse(store.state.walletState.addressData?.exploreUrl)
|
||||
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||
ContextCompat.startActivity(action.context, intent, null)
|
||||
}
|
||||
is WalletAction.Send -> {
|
||||
val newAction = prepareSendAction(action.amount)
|
||||
store.dispatch(newAction)
|
||||
if (newAction is PrepareSendScreen) {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Send))
|
||||
}
|
||||
}
|
||||
is WalletAction.CheckIfWarningNeeded -> {
|
||||
val card = store.state.globalState.scanNoteResponse?.card
|
||||
val validator = store.state.globalState.scanNoteResponse?.walletManager
|
||||
as? SignatureCountValidator
|
||||
if (card != null && !preferencesStorage.wasCardScannedBefore(card.cardId)) {
|
||||
val result = checkIfWarningNeeded(card, validator)
|
||||
if (result != null) store.dispatch(WalletAction.ShowWarning(result))
|
||||
}
|
||||
}
|
||||
is WalletAction.CheckHashesCountOnline -> checkHashesCountOnline()
|
||||
is WalletAction.SaveCardId -> {
|
||||
val cardId = store.state.globalState.scanNoteResponse?.card?.cardId
|
||||
cardId?.let { preferencesStorage.saveScannedCardId(it) }
|
||||
}
|
||||
}
|
||||
is WalletAction.ExploreAddress -> {
|
||||
val uri = Uri.parse(store.state.walletState.addressData?.exploreUrl)
|
||||
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||
ContextCompat.startActivity(action.context, intent, null)
|
||||
}
|
||||
is WalletAction.Send -> {
|
||||
val newAction = prepareSendAction(action.amount)
|
||||
store.dispatch(newAction)
|
||||
if (newAction is PrepareSendScreen) {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Send))
|
||||
}
|
||||
}
|
||||
is WalletAction.CheckIfWarningNeeded -> {
|
||||
val card = store.state.globalState.scanNoteResponse?.card
|
||||
val validator = store.state.globalState.scanNoteResponse?.walletManager
|
||||
as? SignatureCountValidator
|
||||
if (card != null && !preferencesStorage.wasCardScannedBefore(card.cardId)) {
|
||||
val result = checkIfWarningNeeded(card, validator)
|
||||
if (result != null) store.dispatch(WalletAction.ShowWarning(result))
|
||||
}
|
||||
}
|
||||
is WalletAction.CheckHashesCountOnline -> checkHashesCountOnline()
|
||||
is WalletAction.SaveCardId -> {
|
||||
val cardId = store.state.globalState.scanNoteResponse?.card?.cardId
|
||||
cardId?.let { preferencesStorage.saveScannedCardId(it) }
|
||||
}
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupWalletUpdate(wallet: Wallet) {
|
||||
if (!wallet.recentTransactions.toPendingTransactions(wallet.address).isNullOrEmpty()) {
|
||||
store.dispatch(WalletAction.UpdateWallet.ScheduleUpdatingWallet)
|
||||
scope.launch(Dispatchers.IO) {
|
||||
delay(10000)
|
||||
withContext(Dispatchers.Main) {
|
||||
store.dispatch(WalletAction.UpdateWallet)
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun prepareSendAction(amount: Amount?): Action {
|
||||
return if (amount != null) {
|
||||
if (amount.type == AmountType.Token) {
|
||||
PrepareSendScreen(store.state.walletState.wallet?.amounts?.get(AmountType.Coin), amount)
|
||||
} else {
|
||||
PrepareSendScreen(amount)
|
||||
}
|
||||
} else {
|
||||
val amounts = store.state.walletState.wallet?.amounts?.toSendableAmounts()
|
||||
if (amounts?.size ?: 0 > 1) {
|
||||
WalletAction.Send.ChooseCurrency(amounts)
|
||||
} else {
|
||||
val amountToSend = amounts?.first()
|
||||
PrepareSendScreen(amountToSend)
|
||||
private fun setupWalletUpdate(wallet: Wallet) {
|
||||
if (!wallet.recentTransactions.toPendingTransactions(wallet.address).isNullOrEmpty()) {
|
||||
store.dispatch(WalletAction.UpdateWallet.ScheduleUpdatingWallet)
|
||||
scope.launch(Dispatchers.IO) {
|
||||
delay(10000)
|
||||
withContext(Dispatchers.Main) {
|
||||
store.dispatch(WalletAction.UpdateWallet)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkIfWarningNeeded(
|
||||
card: Card, signatureCountValidator: SignatureCountValidator? = null
|
||||
): WarningType? {
|
||||
|
||||
if (card.getType() != CardType.Release) {
|
||||
return WarningType.DevCard
|
||||
private fun prepareSendAction(amount: Amount?): Action {
|
||||
return if (amount != null) {
|
||||
if (amount.type == AmountType.Token) {
|
||||
PrepareSendScreen(store.state.walletState.wallet?.amounts?.get(AmountType.Coin), amount)
|
||||
} else {
|
||||
PrepareSendScreen(amount)
|
||||
}
|
||||
} else {
|
||||
val amounts = store.state.walletState.wallet?.amounts?.toSendableAmounts()
|
||||
if (amounts?.size ?: 0 > 1) {
|
||||
WalletAction.Send.ChooseCurrency(amounts)
|
||||
} else {
|
||||
val amountToSend = amounts?.first()
|
||||
PrepareSendScreen(amountToSend)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return if (signatureCountValidator == null) {
|
||||
if (card.walletSignedHashes ?: 0 > 0) {
|
||||
WarningType.CardSignedHashesBefore
|
||||
private fun checkIfWarningNeeded(
|
||||
card: Card, signatureCountValidator: SignatureCountValidator? = null
|
||||
): WarningType? {
|
||||
|
||||
if (card.getType() != CardType.Release) {
|
||||
return WarningType.DevCard
|
||||
}
|
||||
|
||||
return if (signatureCountValidator == null) {
|
||||
if (card.walletSignedHashes ?: 0 > 0) {
|
||||
WarningType.CardSignedHashesBefore
|
||||
} else {
|
||||
store.dispatch(WalletAction.SaveCardId)
|
||||
null
|
||||
}
|
||||
} else {
|
||||
store.dispatch(WalletAction.SaveCardId)
|
||||
store.dispatch(WalletAction.NeedToCheckHashesCountOnline)
|
||||
null
|
||||
}
|
||||
} else {
|
||||
store.dispatch(WalletAction.NeedToCheckHashesCountOnline)
|
||||
null
|
||||
}
|
||||
|
||||
private fun checkHashesCountOnline() {
|
||||
if (store.state.walletState.hashesCountVerified != false) return
|
||||
if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) return
|
||||
|
||||
val card = store.state.globalState.scanNoteResponse?.card
|
||||
if (card == null || preferencesStorage.wasCardScannedBefore(card.cardId)) return
|
||||
|
||||
val validator = store.state.globalState.scanNoteResponse?.walletManager
|
||||
as? SignatureCountValidator
|
||||
scope.launch {
|
||||
val result = validator?.validateSignatureCount(card.walletSignedHashes ?: 0)
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
SimpleResult.Success -> {
|
||||
store.dispatch(WalletAction.ConfirmHashesCount)
|
||||
store.dispatch(WalletAction.SaveCardId)
|
||||
}
|
||||
is SimpleResult.Failure ->
|
||||
if (result.error is BlockchainSdkError.SignatureCountNotMatched) {
|
||||
store.dispatch(WalletAction.ShowWarning(WarningType.CardSignedHashesBefore))
|
||||
} else if (card.walletSignedHashes ?: 0 > 0) {
|
||||
store.dispatch(WalletAction.ShowWarning(WarningType.CardSignedHashesBefore))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkHashesCountOnline() {
|
||||
if (store.state.walletState.hashesCountVerified != false) return
|
||||
if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) return
|
||||
private class TopUpMiddleware {
|
||||
fun handle(action: WalletAction.TopUpAction) {
|
||||
when (action) {
|
||||
is WalletAction.TopUpAction.TopUp -> {
|
||||
val url = TopUpHelper.getUrl(
|
||||
store.state.walletState.currencyData.currencySymbol!!,
|
||||
store.state.walletState.addressData!!.address
|
||||
)
|
||||
Timber.d(url)
|
||||
store.dispatch(WalletAction.TopUpAction.Start(url, TopUpHelper.REDIRECT_URL))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.TopUp))
|
||||
}
|
||||
is WalletAction.TopUpAction.Start -> {
|
||||
|
||||
val card = store.state.globalState.scanNoteResponse?.card
|
||||
if (card == null || preferencesStorage.wasCardScannedBefore(card.cardId)) return
|
||||
|
||||
val validator = store.state.globalState.scanNoteResponse?.walletManager
|
||||
as? SignatureCountValidator
|
||||
scope.launch {
|
||||
val result = validator?.validateSignatureCount(card.walletSignedHashes ?: 0)
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
SimpleResult.Success -> {
|
||||
store.dispatch(WalletAction.ConfirmHashesCount)
|
||||
store.dispatch(WalletAction.SaveCardId)
|
||||
}
|
||||
is SimpleResult.Failure ->
|
||||
if (result.error is BlockchainSdkError.SignatureCountNotMatched) {
|
||||
store.dispatch(WalletAction.ShowWarning(WarningType.CardSignedHashesBefore))
|
||||
} else if (card.walletSignedHashes ?: 0 > 0) {
|
||||
store.dispatch(WalletAction.ShowWarning(WarningType.CardSignedHashesBefore))
|
||||
}
|
||||
}
|
||||
is WalletAction.TopUpAction.Finish -> {
|
||||
if (action.topUpCompleted) store.dispatch(WalletAction.UpdateWallet)
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
currencyData = BalanceWidgetData(
|
||||
BalanceStatus.Loading,
|
||||
wallet?.blockchain?.fullName,
|
||||
currencySymbol = wallet?.blockchain?.currency,
|
||||
token = wallet?.token?.symbol?.let {
|
||||
TokenData("", tokenSymbol = it)
|
||||
}
|
||||
|
|
@ -210,11 +211,25 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
newState = newState.copy(hashesCountVerified = false)
|
||||
is WalletAction.ConfirmHashesCount ->
|
||||
newState = newState.copy(hashesCountVerified = true)
|
||||
is WalletAction.TopUpAction -> {
|
||||
newState = newState.copy(topUpState = handleTopUpActions(action, newState.topUpState))
|
||||
}
|
||||
}
|
||||
return newState
|
||||
}
|
||||
|
||||
private fun onWalletLoaded(wallet: Wallet, walletState: WalletState): WalletState {
|
||||
private fun handleTopUpActions(action: WalletAction.TopUpAction, state: TopUpState): TopUpState {
|
||||
return when (action) {
|
||||
is WalletAction.TopUpAction.TopUp -> state
|
||||
is WalletAction.TopUpAction.Start ->
|
||||
state.copy(url = action.url, redirectUrl = action.redirectUrl)
|
||||
is WalletAction.TopUpAction.Finish -> state.copy(url = null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onWalletLoaded(
|
||||
wallet: Wallet, walletState: WalletState, topUpAllowed: Boolean? = null
|
||||
): WalletState {
|
||||
val fiatCurrencySymbol = store.state.globalState.appCurrency
|
||||
val token = wallet.amounts[AmountType.Token]
|
||||
val tokenData = if (token != null) {
|
||||
|
|
@ -242,6 +257,7 @@ private fun onWalletLoaded(wallet: Wallet, walletState: WalletState): WalletStat
|
|||
} else {
|
||||
BalanceStatus.VerifiedOnline
|
||||
}
|
||||
val topUpState = topUpAllowed?.let { TopUpState(topUpAllowed) } ?: walletState.topUpState
|
||||
return walletState.copy(
|
||||
state = ProgressState.Done, wallet = wallet,
|
||||
currencyData = BalanceWidgetData(
|
||||
|
|
@ -252,6 +268,7 @@ private fun onWalletLoaded(wallet: Wallet, walletState: WalletState): WalletStat
|
|||
fiatAmount = fiatAmount
|
||||
),
|
||||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(sendButtonEnabled)
|
||||
mainButton = WalletMainButton.SendButton(sendButtonEnabled),
|
||||
topUpState = topUpState
|
||||
)
|
||||
}
|
||||
|
|
@ -21,7 +21,8 @@ data class WalletState(
|
|||
val payIdData: PayIdData = PayIdData(),
|
||||
val walletDialog: WalletDialog? = null,
|
||||
val updatingWallet: Boolean = false,
|
||||
val mainButton: WalletMainButton = WalletMainButton.SendButton(false)
|
||||
val mainButton: WalletMainButton = WalletMainButton.SendButton(false),
|
||||
val topUpState: TopUpState = TopUpState()
|
||||
) : StateType {
|
||||
val showDetails: Boolean =
|
||||
currencyData.status != com.tangem.tap.features.wallet.ui.BalanceStatus.EmptyCard &&
|
||||
|
|
@ -76,4 +77,10 @@ data class Artwork(
|
|||
const val SERGIO_CARD_ID = "BC01"
|
||||
const val MARTA_CARD_ID = "BC02"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class TopUpState(
|
||||
val allowed: Boolean = true,
|
||||
val url: String? = null,
|
||||
val redirectUrl: String? = null
|
||||
)
|
||||
|
|
@ -170,6 +170,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
}
|
||||
btn_confirm.text = getString(buttonTitle)
|
||||
btn_confirm.isEnabled = state.mainButton.enabled
|
||||
btn_top_up.isEnabled = state.topUpState.allowed
|
||||
|
||||
btn_confirm.setOnClickListener {
|
||||
when (state.mainButton) {
|
||||
|
|
@ -177,6 +178,11 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
is WalletMainButton.CreateWalletButton -> store.dispatch(WalletAction.CreateWallet)
|
||||
}
|
||||
}
|
||||
btn_top_up.setOnClickListener {
|
||||
store.dispatch(
|
||||
WalletAction.TopUpAction.TopUp(requireContext(), R.color.backgroundLightGray)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupAddressCard(state: WalletState) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
package com.tangem.tap.features.wallet.ui.topup
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.fragment_topup.*
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
|
||||
class TopUpFragment : Fragment(R.layout.fragment_topup), StoreSubscriber<WalletState> {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
})
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.walletState == newState.walletState
|
||||
}.select { it.walletState }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
web_view.settings.javaScriptEnabled = true
|
||||
|
||||
toolbar.setNavigationOnClickListener {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun newState(state: WalletState) {
|
||||
if (activity == null) return
|
||||
|
||||
if (state.topUpState.url != null) {
|
||||
web_view.webViewClient = TopUpWebViewClient(progress_bar, state.topUpState.redirectUrl)
|
||||
web_view.loadUrl(state.topUpState.url)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.tap.features.wallet.ui.topup
|
||||
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import android.widget.ProgressBar
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.store
|
||||
|
||||
class TopUpWebViewClient(
|
||||
private val progressBar: ProgressBar, private val redirectUrl: String?
|
||||
) : WebViewClient() {
|
||||
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
|
||||
if (redirectUrl != null && url.contains(redirectUrl)) {
|
||||
store.dispatch(WalletAction.TopUpAction.Finish(true))
|
||||
return true
|
||||
}
|
||||
view.loadUrl(url)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPageFinished(view: WebView, url: String) {
|
||||
super.onPageFinished(view, url)
|
||||
progressBar.hide()
|
||||
}
|
||||
|
||||
init {
|
||||
progressBar.show()
|
||||
}
|
||||
}
|
||||
17
app/src/main/res/drawable/ic_arrow_up.xml
Normal file
17
app/src/main/res/drawable/ic_arrow_up.xml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="14dp"
|
||||
android:height="14dp"
|
||||
android:viewportWidth="10"
|
||||
android:viewportHeight="12">
|
||||
<group
|
||||
android:scaleX="0.8"
|
||||
android:pivotX="5.3"
|
||||
android:pivotY="6"
|
||||
android:rotation="180"
|
||||
>
|
||||
<path
|
||||
android:pathData="M5.853,11.6645L9.7814,8.075C10.0495,7.8439 10.0734,7.3871 9.84,7.1191C9.6067,6.8511 9.1615,6.824 8.9084,7.0724L6.0712,9.6665L6.0712,0.8398C6.0712,0.468 5.7781,0.1667 5.4165,0.1667C5.0549,0.1667 4.7618,0.468 4.7618,0.8398L4.7618,9.6665L1.9246,7.0724C1.6715,6.824 1.2271,6.8576 0.9937,7.1256C0.7604,7.3936 0.7835,7.8439 1.0516,8.075L4.98,11.6645C5.2976,11.8989 5.5827,11.8802 5.853,11.6645L5.853,11.6645Z"
|
||||
android:fillColor="#1C1C1E"
|
||||
/>
|
||||
</group>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_arrow_up_short_btn.xml
Normal file
9
app/src/main/res/drawable/ic_arrow_up_short_btn.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="19dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="19">
|
||||
<path
|
||||
android:pathData="M11.4765,5.7026L6.7623,10.0101C6.4406,10.2874 6.412,10.8356 6.692,11.1572C6.972,11.4788 7.5063,11.5112 7.8099,11.2131L11.2146,8.1003L11.2146,18.6923C11.2146,19.1384 11.5664,19.5 12.0003,19.5C12.4342,19.5 12.7859,19.1384 12.7859,18.6923L12.7859,8.1003L16.1906,11.2131C16.4942,11.5112 17.0275,11.4709 17.3076,11.1493C17.5876,10.8277 17.5599,10.2874 17.2382,10.0101L12.524,5.7026C12.1429,5.4214 11.8008,5.4438 11.4765,5.7026L11.4765,5.7026Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_close.xml
Normal file
10
app/src/main/res/drawable/ic_close.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
</vector>
|
||||
|
|
@ -1,15 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="13dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="13"
|
||||
android:viewportHeight="18">
|
||||
android:width="24dp"
|
||||
android:height="21dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="21"
|
||||
>
|
||||
<path
|
||||
android:pathData="M11.7908,6.0129C10.9896,3.7531 9.3666,1.9247 7.1993,0.8873C5.032,-0.1502 2.5873,-0.2837 0.3275,0.5175L0.2762,0.538C0.1734,0.5791 0.081,0.651 0.0399,0.764C-0.0114,0.8667 -0.0114,0.99 0.0296,1.0927C0.1221,1.329 0.3789,1.442 0.6254,1.3598C2.6695,0.6408 4.8676,0.764 6.809,1.6988C8.7606,2.6335 10.2295,4.277 10.9485,6.3108C11.2566,7.1839 11.4107,8.0878 11.4107,9.002C11.4107,9.9162 11.2566,10.8201 10.9485,11.6932C10.2295,13.7372 8.7606,15.3704 6.809,16.3052C4.8573,17.2399 2.6592,17.3632 0.6151,16.6441C0.3789,16.562 0.1221,16.6749 0.0296,16.9112C-0.0114,17.0242 -0.0114,17.1372 0.0399,17.2399C0.0913,17.3426 0.1734,17.4248 0.2864,17.4659L0.3378,17.4864C1.3136,17.8254 2.3202,18 3.3269,18C4.6519,18 5.977,17.7021 7.2096,17.1166C9.3769,16.0792 11.0101,14.2611 11.801,11.991C12.14,11.0255 12.3146,10.0189 12.3146,9.0122C12.3044,7.9851 12.1297,6.9784 11.7908,6.0129Z"
|
||||
android:pathData="M17.7908,9.0129C16.9896,6.7531 15.3666,4.9247 13.1993,3.8873C11.032,2.8498 8.5873,2.7163 6.3275,3.5175L6.2762,3.538C6.1734,3.5791 6.081,3.651 6.0399,3.764C5.9886,3.8667 5.9886,3.99 6.0296,4.0927C6.1221,4.329 6.3789,4.442 6.6254,4.3598C8.6695,3.6408 10.8676,3.764 12.809,4.6988C14.7606,5.6335 16.2295,7.277 16.9485,9.3108C17.2566,10.1839 17.4107,11.0878 17.4107,12.002C17.4107,12.9161 17.2566,13.8201 16.9485,14.6932C16.2295,16.7372 14.7606,18.3704 12.809,19.3052C10.8574,20.2399 8.6592,20.3632 6.6151,19.6441C6.3789,19.562 6.1221,19.6749 6.0296,19.9112C5.9886,20.0242 5.9886,20.1372 6.0399,20.2399C6.0913,20.3426 6.1734,20.4248 6.2864,20.4659L6.3378,20.4864C7.3136,20.8254 8.3202,21 9.3269,21C10.6519,21 11.977,20.7021 13.2096,20.1166C15.3769,19.0792 17.0101,17.2611 17.801,14.991C18.14,14.0255 18.3146,13.0189 18.3146,12.0122C18.3044,10.9851 18.1297,9.9784 17.7908,9.0129Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M1.1703,14.0252C1.1183,14.1498 1.1287,14.2848 1.1806,14.4095C1.2326,14.5341 1.3364,14.6172 1.4715,14.6588C2.0532,14.8457 2.6556,14.9392 3.2581,14.9392C3.9644,14.9392 4.6707,14.8146 5.3251,14.5549C6.7689,14.0044 7.9115,12.9137 8.5347,11.5011C8.8671,10.7532 9.0333,9.9742 9.0333,9.1744V9.0082C9.0126,8.2499 8.8464,7.502 8.5347,6.7957C7.9011,5.383 6.7689,4.3028 5.3251,3.7419C4.0787,3.2641 2.6972,3.2329 1.4299,3.6484C1.3157,3.6899 1.2118,3.773 1.1599,3.8873C1.1079,4.0016 1.1079,4.1262 1.1495,4.2405C1.2326,4.4586 1.4819,4.5729 1.7104,4.5001C2.7803,4.147 3.954,4.1677 5.0031,4.5729C6.2184,5.0403 7.1844,5.9543 7.7142,7.1489C7.9946,7.7721 8.1296,8.4265 8.14,9.1017V9.1328C8.14,9.8184 7.9946,10.4831 7.7142,11.1167C7.1844,12.3113 6.2184,13.2253 5.0031,13.6928C3.9436,14.0979 2.7699,14.1186 1.6896,13.7551C1.4819,13.7135 1.243,13.8174 1.1703,14.0252Z"
|
||||
android:pathData="M7.1702,17.0252C7.1183,17.1498 7.1287,17.2848 7.1806,17.4095C7.2326,17.5341 7.3365,17.6172 7.4715,17.6588C8.0532,17.8457 8.6556,17.9392 9.2581,17.9392C9.9644,17.9392 10.6707,17.8146 11.3251,17.5549C12.7689,17.0044 13.9115,15.9137 14.5347,14.5011C14.8671,13.7532 15.0333,12.9742 15.0333,12.1744V12.0082C15.0126,11.2499 14.8464,10.502 14.5347,9.7957C13.9011,8.383 12.7689,7.3028 11.3251,6.7419C10.0787,6.2641 8.6972,6.2329 7.4299,6.6484C7.3157,6.6899 7.2118,6.773 7.1599,6.8873C7.1079,7.0016 7.1079,7.1262 7.1495,7.2405C7.2326,7.4586 7.4819,7.5729 7.7104,7.5001C8.7803,7.147 9.954,7.1677 11.0031,7.5729C12.2184,8.0403 13.1844,8.9543 13.7142,10.1489C13.9946,10.7721 14.1296,11.4265 14.14,12.1017V12.1328C14.14,12.8184 13.9946,13.4831 13.7142,14.1167C13.1844,15.3113 12.2184,16.2253 11.0031,16.6928C9.9436,17.0979 8.7699,17.1186 7.6896,16.7551C7.4819,16.7135 7.243,16.8174 7.1702,17.0252Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M3.9148,10.5778C3.6136,10.692 3.2812,10.7128 2.9592,10.6297C2.7618,10.5778 2.5437,10.6816 2.471,10.879V10.8894C2.419,11.014 2.4294,11.1594 2.5021,11.2841C2.5645,11.4087 2.6891,11.5022 2.8345,11.5334C3.0111,11.5749 3.1773,11.5853 3.3539,11.5853C3.6551,11.5853 3.9563,11.5334 4.2472,11.4191C5.2028,11.0556 5.8468,10.1207 5.8468,9.0924C5.8468,8.0641 5.2028,7.1396 4.2472,6.7657C3.7694,6.5787 3.25,6.5476 2.741,6.6722C2.6268,6.7034 2.5229,6.7761 2.471,6.8903C2.419,6.9942 2.4086,7.1188 2.4502,7.2331L2.4606,7.2539C2.5437,7.472 2.7722,7.5862 3.0007,7.5447C3.3019,7.472 3.624,7.4928 3.9148,7.607C4.5276,7.8356 4.9327,8.438 4.9327,9.0924C4.9327,9.7468 4.5276,10.3492 3.9148,10.5778Z"
|
||||
android:pathData="M9.9148,13.5778C9.6136,13.692 9.2812,13.7128 8.9592,13.6297C8.7619,13.5778 8.5437,13.6816 8.471,13.879V13.8894C8.4191,14.014 8.4295,14.1594 8.5022,14.2841C8.5645,14.4087 8.6892,14.5022 8.8346,14.5334C9.0112,14.5749 9.1774,14.5853 9.3539,14.5853C9.6552,14.5853 9.9564,14.5334 10.2472,14.4191C11.2029,14.0556 11.8469,13.1207 11.8469,12.0924C11.8469,11.0641 11.2029,10.1396 10.2472,9.7657C9.7694,9.5787 9.2501,9.5476 8.7411,9.6722C8.6268,9.7034 8.523,9.7761 8.471,9.8903C8.4191,9.9942 8.4087,10.1188 8.4503,10.2331L8.4607,10.2539C8.5437,10.472 8.7723,10.5863 9.0008,10.5447C9.302,10.472 9.624,10.4928 9.9148,10.607C10.5277,10.8356 10.9328,11.438 10.9328,12.0924C10.9328,12.7468 10.5277,13.3492 9.9148,13.5778Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
||||
|
|
|
|||
9
app/src/main/res/drawable/ic_send_short_btn.xml
Normal file
9
app/src/main/res/drawable/ic_send_short_btn.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="19dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="19">
|
||||
<path
|
||||
android:pathData="M18.7974,10.9765L14.4899,6.2623C14.2126,5.9406 13.6644,5.912 13.3428,6.192C13.0212,6.472 12.9888,7.0063 13.2869,7.3099L16.3997,10.7146L5.8077,10.7146C5.3616,10.7146 5,11.0664 5,11.5003C5,11.9342 5.3616,12.2859 5.8077,12.2859L16.3997,12.2859L13.2869,15.6906C12.9888,15.9942 13.0291,16.5275 13.3507,16.8076C13.6723,17.0876 14.2126,17.0599 14.4899,16.7382L18.7974,12.024C19.0786,11.6429 19.0562,11.3008 18.7974,10.9765L18.7974,10.9765Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
||||
|
|
@ -63,7 +63,6 @@
|
|||
android:id="@+id/btn_shop"
|
||||
style="@style/TapBlackButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginBottom="33dp"
|
||||
android:text="@string/common_no"
|
||||
|
|
@ -85,7 +84,6 @@
|
|||
android:id="@+id/btn_yes"
|
||||
style="@style/TapButtonWithIcon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="33dp"
|
||||
|
|
|
|||
|
|
@ -104,7 +104,6 @@
|
|||
android:id="@+id/btnSend"
|
||||
style="@style/TapButtonWithIcon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:fontFamily="@font/saira_semi_condensed_regular"
|
||||
android:text="@string/send_title"
|
||||
app:icon="@drawable/ic_arrow_right" />
|
||||
|
|
|
|||
31
app/src/main/res/layout/fragment_topup.xml
Normal file
31
app/src/main/res/layout/fragment_topup.xml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:fitsSystemWindows="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navigationIcon="@drawable/ic_close"
|
||||
app:title="@string/wallet_button_topup" />
|
||||
|
||||
<WebView
|
||||
android:id="@+id/web_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
style="@android:style/Widget.Material.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminateOnly="true"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -49,7 +49,6 @@
|
|||
android:id="@+id/iv_card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxHeight="215dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
|
|
@ -57,6 +56,7 @@
|
|||
android:layout_marginBottom="12dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:elevation="3dp"
|
||||
android:maxHeight="215dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/card_placeholder"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
|
@ -90,40 +90,49 @@
|
|||
android:id="@+id/btn_scan"
|
||||
style="@style/TapBlackButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:text="@string/wallet_button_scan"
|
||||
app:icon="@drawable/ic_scan"
|
||||
android:gravity="bottom|center_horizontal"
|
||||
android:drawableTop="@drawable/ic_scan"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintEnd_toStartOf="@id/btn_top_up"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/l_address"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_top_up"
|
||||
style="@style/TapButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:text="@string/wallet_button_topup"
|
||||
android:drawableTop="@drawable/ic_arrow_up_short_btn"
|
||||
app:drawableTint="@android:color/white"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintGuide_percent="0.37" />
|
||||
app:layout_constraintEnd_toStartOf="@+id/btn_confirm"
|
||||
app:layout_constraintStart_toEndOf="@+id/btn_scan"
|
||||
app:layout_constraintTop_toBottomOf="@id/l_address"
|
||||
app:layout_constraintVertical_bias="1"
|
||||
android:gravity="bottom|center_horizontal"
|
||||
/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_confirm"
|
||||
style="@style/TapButtonWithIcon"
|
||||
style="@style/TapButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/wallet_button_send"
|
||||
app:icon="@drawable/ic_send"
|
||||
android:drawableTop="@drawable/ic_send_short_btn"
|
||||
|
||||
android:gravity="bottom|center_horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
app:layout_constraintStart_toEndOf="@+id/btn_top_up"
|
||||
app:layout_constraintTop_toBottomOf="@id/l_address"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
|
|
|
|||
|
|
@ -3,5 +3,6 @@
|
|||
<string name="xtz_withdrawal_message_warning" translatable="false">To avoid paying an increased commission the next time you top up your wallet, reduce the amount by %s XTZ</string>
|
||||
<string name="xtz_withdrawal_message_reduce" translatable="false">Reduce by %s XTZ</string>
|
||||
<string name="xtz_withdrawal_message_ignore" translatable="false">No, send all</string>
|
||||
<string name="wallet_button_topup" translatable="false">Top up</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
<item name="cornerRadius">@dimen/btn_corner_radius</item>
|
||||
<item name="android:fontFamily">@font/saira_semi_condensed_regular</item>
|
||||
<item name="android:layout_width">93dp</item>
|
||||
<item name="android:layout_height">48dp</item>
|
||||
<item name="android:layout_height">52dp</item>
|
||||
<item name="elevation">3dp</item>
|
||||
</style>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue