Updated on 2026-08-14
This commit is contained in:
commit
f21c4adfd2
30 changed files with 646 additions and 75 deletions
|
|
@ -34,7 +34,8 @@
|
|||
<activity
|
||||
android:name="com.tangem.tap.MainActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait">
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import android.os.Bundle
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.tangem.CardFilter
|
||||
import com.tangem.Config
|
||||
import com.tangem.Log
|
||||
import com.tangem.TangemSdk
|
||||
import com.tangem.common.extensions.CardType
|
||||
import com.tangem.tangem_sdk_new.extensions.init
|
||||
|
|
@ -18,6 +19,8 @@ import kotlinx.coroutines.CoroutineExceptionHandler
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
|
@ -32,7 +35,13 @@ val scope = CoroutineScope(coroutineContext)
|
|||
|
||||
|
||||
private fun initCoroutineExceptionHandler(): CoroutineExceptionHandler {
|
||||
return CoroutineExceptionHandler { _, throwable -> throw throwable }
|
||||
return CoroutineExceptionHandler { _, throwable ->
|
||||
val sw = StringWriter()
|
||||
throwable.printStackTrace(PrintWriter(sw))
|
||||
val exceptionAsString: String = sw.toString()
|
||||
Log.e("TangemSdk", exceptionAsString)
|
||||
throw throwable
|
||||
}
|
||||
}
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
|
|
|||
6
app/src/main/java/com/tangem/tap/TapConfig.kt
Normal file
6
app/src/main/java/com/tangem/tap/TapConfig.kt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.tap
|
||||
|
||||
object TapConfig {
|
||||
const val usePayId: Boolean = true
|
||||
const val coinMarketCapKey = "f6622117-c043-47a0-8975-9d673ce484de"
|
||||
}
|
||||
|
|
@ -6,6 +6,11 @@ import com.google.zxing.BarcodeFormat
|
|||
import com.google.zxing.EncodeHintType
|
||||
import com.google.zxing.qrcode.QRCodeWriter
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import java.text.DecimalFormat
|
||||
import java.text.DecimalFormatSymbols
|
||||
import java.util.*
|
||||
|
||||
|
||||
|
|
@ -27,4 +32,23 @@ fun String.toQrCode(): Bitmap {
|
|||
}
|
||||
}
|
||||
return bmp
|
||||
}
|
||||
|
||||
fun BigDecimal.toFormattedString(blockchain: Blockchain): String {
|
||||
val symbols = DecimalFormatSymbols(Locale.US)
|
||||
symbols.decimalSeparator = '.'
|
||||
val df = DecimalFormat()
|
||||
df.decimalFormatSymbols = symbols
|
||||
df.maximumFractionDigits = blockchain.decimals()
|
||||
df.minimumFractionDigits = 0
|
||||
df.isGroupingUsed = false
|
||||
val bd = BigDecimal(unscaledValue(), scale())
|
||||
bd.setScale(blockchain.decimals(), BigDecimal.ROUND_DOWN)
|
||||
return df.format(bd)
|
||||
}
|
||||
|
||||
fun BigDecimal.toFiatString(rateValue: BigDecimal): String? {
|
||||
var fiatValue = rateValue.multiply(this)
|
||||
fiatValue = fiatValue.setScale(2, RoundingMode.DOWN)
|
||||
return "≈ USD $fiatValue"
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.common.redux
|
||||
|
||||
import com.tangem.tap.common.redux.global.globalReducer
|
||||
import com.tangem.tap.common.redux.navigation.navigationReducer
|
||||
import com.tangem.tap.features.wallet.redux.walletReducer
|
||||
import org.rekotlin.Action
|
||||
|
|
@ -8,9 +9,9 @@ fun appReducer(action: Action, state: AppState?): AppState {
|
|||
requireNotNull(state)
|
||||
if (action is AppAction.RestoreState) return action.state
|
||||
return AppState(
|
||||
navigationState = navigationReducer(action, state),
|
||||
// homeState = homeReducer(action, state),
|
||||
walletState = walletReducer(action, state),
|
||||
navigationState = navigationReducer(action, state),
|
||||
globalState = globalReducer(action, state),
|
||||
walletState = walletReducer(action, state),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.common.redux
|
||||
|
||||
import com.tangem.tap.common.redux.global.GlobalState
|
||||
import com.tangem.tap.common.redux.navigation.NavigationState
|
||||
import com.tangem.tap.common.redux.navigation.navigationMiddleware
|
||||
import com.tangem.tap.features.home.redux.homeMiddleware
|
||||
|
|
@ -9,8 +10,9 @@ import org.rekotlin.Middleware
|
|||
import org.rekotlin.StateType
|
||||
|
||||
data class AppState(
|
||||
val navigationState: NavigationState = NavigationState(),
|
||||
val walletState: WalletState = WalletState()
|
||||
val navigationState: NavigationState = NavigationState(),
|
||||
val globalState: GlobalState = GlobalState(),
|
||||
val walletState: WalletState = WalletState()
|
||||
) : StateType {
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -2,24 +2,33 @@ package com.tangem.tap.common.redux
|
|||
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.TangemError
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.notificationsHandler
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class NotificationsHandler(coordinatorLayout: CoordinatorLayout) {
|
||||
private val coordinatorLayoutWeak = WeakReference(coordinatorLayout)
|
||||
private val basicCoordinatorLayout = WeakReference(coordinatorLayout)
|
||||
private var baseLayout = basicCoordinatorLayout
|
||||
|
||||
fun replaceBaseLayout(coordinatorLayout: CoordinatorLayout) {
|
||||
baseLayout = WeakReference(coordinatorLayout)
|
||||
}
|
||||
|
||||
fun returnBaseLayout() {
|
||||
baseLayout = basicCoordinatorLayout
|
||||
}
|
||||
|
||||
fun showNotification(message: String) {
|
||||
coordinatorLayoutWeak.get()?.let { layout ->
|
||||
baseLayout.get()?.let { layout ->
|
||||
Snackbar.make(layout, message, Snackbar.LENGTH_LONG)
|
||||
.also { snackbar -> snackbar.show() }
|
||||
.also { snackbar -> snackbar.show() }
|
||||
}
|
||||
}
|
||||
|
||||
fun showNotification(message: Int) {
|
||||
coordinatorLayoutWeak.get()?.let {
|
||||
baseLayout.get()?.let {
|
||||
showNotification(it.context.getString(message))
|
||||
}
|
||||
}
|
||||
|
|
@ -32,7 +41,7 @@ val notificationsMiddleware: Middleware<AppState> = { dispatch, state ->
|
|||
notificationsHandler?.showNotification(action.messageResource)
|
||||
}
|
||||
if (action is ErrorAction) {
|
||||
notificationsHandler?.showNotification(action.error.customMessage)
|
||||
notificationsHandler?.showNotification(action.error.localizedMessage)
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
|
|
@ -44,5 +53,5 @@ interface NotificationAction : Action {
|
|||
}
|
||||
|
||||
interface ErrorAction : Action {
|
||||
val error: TangemError
|
||||
val error: TapError
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.tap.common.redux.global
|
||||
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.commands.Card
|
||||
import org.rekotlin.Action
|
||||
|
||||
sealed class GlobalAction : Action {
|
||||
|
||||
data class LoadCard(val card: Card) : GlobalAction()
|
||||
data class LoadWalletManager(val walletManager: WalletManager) : GlobalAction()
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.tap.common.redux.global
|
||||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import org.rekotlin.Action
|
||||
|
||||
fun globalReducer(action: Action, state: AppState): GlobalState {
|
||||
|
||||
if (action !is GlobalAction) return state.globalState
|
||||
|
||||
var newState = state.globalState
|
||||
|
||||
when (action) {
|
||||
is GlobalAction.LoadCard -> newState = newState.copy(card = action.card)
|
||||
is GlobalAction.LoadWalletManager ->
|
||||
newState = newState.copy(walletManager = action.walletManager)
|
||||
|
||||
}
|
||||
return newState
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.tap.common.redux.global
|
||||
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.commands.Card
|
||||
import org.rekotlin.StateType
|
||||
|
||||
data class GlobalState(
|
||||
val card: Card? = null,
|
||||
val walletManager: WalletManager? = null,
|
||||
) : StateType
|
||||
|
||||
|
||||
|
||||
67
app/src/main/java/com/tangem/tap/domain/PayIdManager.kt
Normal file
67
app/src/main/java/com/tangem/tap/domain/PayIdManager.kt
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.commands.common.network.Result
|
||||
import com.tangem.tap.network.payid.PayIdService
|
||||
import com.tangem.tap.network.payid.SetPayIdResponse
|
||||
import retrofit2.HttpException
|
||||
import java.util.*
|
||||
|
||||
class PayIdManager {
|
||||
private val payIdService = PayIdService()
|
||||
|
||||
suspend fun getPayId(cardId: String, publicKey: String): Result<String?> {
|
||||
val result = payIdService.getPayId(cardId, publicKey)
|
||||
when (result) {
|
||||
is Result.Success -> return Result.Success(result.data.payId)
|
||||
is Result.Failure -> {
|
||||
(result.error as? HttpException)?.let {
|
||||
if (it.code() == 404) return Result.Success(null)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun setPayId(
|
||||
cardId: String, publicKey: String, payId: String, address: String, blockchain: Blockchain
|
||||
): Result<SetPayIdResponse> {
|
||||
val result = payIdService.setPayId(cardId, publicKey, payId, address, blockchain.getPayIdNetwork())
|
||||
when (result) {
|
||||
is Result.Success -> return result
|
||||
is Result.Failure -> {
|
||||
(result.error as? HttpException)?.let {
|
||||
if (it.code() == 409) return Result.Failure(TapError.PayIdAlreadyCreated)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Blockchain.getPayIdNetwork(): String {
|
||||
return when (this) {
|
||||
Blockchain.XRP -> "XRPL"
|
||||
Blockchain.RSK -> "RSK"
|
||||
else -> this.currency
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val payIdSupported: EnumSet<Blockchain> = EnumSet.of(
|
||||
Blockchain.XRP,
|
||||
Blockchain.Ethereum,
|
||||
Blockchain.Bitcoin,
|
||||
Blockchain.Litecoin,
|
||||
Blockchain.Stellar,
|
||||
Blockchain.Cardano,
|
||||
Blockchain.Ducatus,
|
||||
Blockchain.BitcoinCash,
|
||||
Blockchain.Binance,
|
||||
Blockchain.RSK,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun Blockchain.isPayIdSupported(): Boolean {
|
||||
return PayIdManager.payIdSupported.contains(this)
|
||||
}
|
||||
10
app/src/main/java/com/tangem/tap/domain/TapErrors.kt
Normal file
10
app/src/main/java/com/tangem/tap/domain/TapErrors.kt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.tangem.wallet.R
|
||||
|
||||
sealed class TapError(@StringRes val localizedMessage: Int): Throwable() {
|
||||
object PayIdAlreadyCreated: TapError(R.string.error_payid_already_created)
|
||||
object PayIdCreatingError: TapError(R.string.error_creating_payid)
|
||||
object PayIdEmptyField: TapError(R.string.wallet_create_payid_empty)
|
||||
}
|
||||
|
|
@ -5,20 +5,25 @@ import com.tangem.CardSessionRunnable
|
|||
import com.tangem.TangemSdkError
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.commands.Card
|
||||
import com.tangem.commands.CommandResponse
|
||||
import com.tangem.common.CompletionResult
|
||||
|
||||
data class ScanNoteResponse(val walletManager: WalletManager) : CommandResponse
|
||||
data class ScanNoteResponse(
|
||||
val walletManager: WalletManager,
|
||||
val card: Card
|
||||
) : CommandResponse
|
||||
|
||||
class ScanNoteTask : CardSessionRunnable<ScanNoteResponse> {
|
||||
override val requiresPin2 = false
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<ScanNoteResponse>) -> Unit) {
|
||||
val walletManager = session.environment.card?.let { WalletManagerFactory.makeWalletManager(it) }
|
||||
if (walletManager == null) {
|
||||
val card = session.environment.card
|
||||
val walletManager = card?.let { WalletManagerFactory.makeWalletManager(it) }
|
||||
if (card == null || walletManager == null) {
|
||||
callback(CompletionResult.Failure(TangemSdkError.CardError()))
|
||||
return
|
||||
}
|
||||
callback(CompletionResult.Success(ScanNoteResponse(walletManager)))
|
||||
callback(CompletionResult.Success(ScanNoteResponse(walletManager, card)))
|
||||
}
|
||||
}
|
||||
|
|
@ -5,13 +5,16 @@ import android.net.Uri
|
|||
import androidx.core.content.ContextCompat.startActivity
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
val homeMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
|
|
@ -21,10 +24,15 @@ val homeMiddleware: Middleware<AppState> = { dispatch, state ->
|
|||
is HomeAction.ReadCard -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.scanNote()
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
store.dispatch(WalletAction.LoadWallet(result.data.walletManager))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
store.dispatch(GlobalAction.LoadCard(result.data.card))
|
||||
store.dispatch(GlobalAction.LoadWalletManager(result.data.walletManager))
|
||||
store.dispatch(WalletAction.LoadWallet)
|
||||
store.dispatch(WalletAction.LoadPayId)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,25 +2,32 @@ package com.tangem.tap.features.wallet.redux
|
|||
|
||||
import android.content.Context
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.tap.common.redux.ErrorAction
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.Action
|
||||
|
||||
sealed class WalletAction : Action {
|
||||
data class LoadWallet(val walletManager: WalletManager) : WalletAction() {
|
||||
object LoadWallet : WalletAction() {
|
||||
data class Success(val wallet: Wallet): WalletAction()
|
||||
object Failure: WalletAction()
|
||||
}
|
||||
data class LoadPayId(val address: String) : WalletAction() {
|
||||
object Success: WalletAction()
|
||||
object LoadPayId : WalletAction() {
|
||||
data class Success(val payId: String): WalletAction()
|
||||
object NotCreated: WalletAction()
|
||||
object Failure: WalletAction()
|
||||
}
|
||||
object Scan : WalletAction()
|
||||
object Send : WalletAction()
|
||||
object CreatePayId : WalletAction() {
|
||||
object Success: WalletAction()
|
||||
object Failure: WalletAction()
|
||||
data class CompleteCreatingPayId(val payId: String): WalletAction()
|
||||
data class Success(val payId: String): WalletAction()
|
||||
object EmptyField: WalletAction(), ErrorAction {
|
||||
override val error = TapError.PayIdEmptyField
|
||||
}
|
||||
class Failure(override val error: TapError) : WalletAction(), ErrorAction
|
||||
object Cancel: WalletAction()
|
||||
}
|
||||
data class CopyAddress(val context: Context) : WalletAction() {
|
||||
object Success : WalletAction(), NotificationAction {
|
||||
|
|
|
|||
|
|
@ -3,9 +3,16 @@ package com.tangem.tap.features.wallet.redux
|
|||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.tangem.commands.common.network.Result
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.tap.TapConfig
|
||||
import com.tangem.tap.common.extensions.copyToClipboard
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.PayIdManager
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.isPayIdSupported
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
|
|
@ -21,15 +28,73 @@ val walletMiddleware: Middleware<AppState> = { dispatch, state ->
|
|||
when (action) {
|
||||
is WalletAction.LoadWallet -> {
|
||||
scope.launch {
|
||||
try {
|
||||
action.walletManager.update()
|
||||
} catch (ex: Exception) {
|
||||
val walletManager = store.state.globalState.walletManager
|
||||
if (walletManager == null) {
|
||||
store.dispatch(WalletAction.LoadWallet.Failure)
|
||||
// callback(CompletionResult.Failure(BlockchainInternalErrorConverter.convert(ex)))
|
||||
return@launch
|
||||
}
|
||||
try {
|
||||
walletManager.update()
|
||||
} catch (ex: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
store.dispatch(WalletAction.LoadWallet.Failure)
|
||||
// callback(CompletionResult.Failure(BlockchainInternalErrorConverter.convert(ex)))
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
store.dispatch(WalletAction.LoadWallet.Success(action.walletManager.wallet))
|
||||
store.dispatch(WalletAction.LoadWallet.Success(walletManager.wallet))
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.LoadPayId -> {
|
||||
if (!TapConfig.usePayId ||
|
||||
store.state.walletState.payIdData.payIdState == PayIdState.Disabled ||
|
||||
store.state.globalState.walletManager?.wallet?.blockchain?.isPayIdSupported() == false) {
|
||||
next(action)
|
||||
}
|
||||
scope.launch {
|
||||
val cardId = store.state.globalState.card?.cardId
|
||||
val publicKey = store.state.globalState.card?.cardPublicKey
|
||||
if (cardId != null && publicKey != null) {
|
||||
val result = PayIdManager().getPayId(cardId, publicKey.toHexString())
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
val payId = result.data
|
||||
if (payId == null) {
|
||||
store.dispatch(WalletAction.LoadPayId.NotCreated)
|
||||
} else {
|
||||
store.dispatch(WalletAction.LoadPayId.Success(payId))
|
||||
}
|
||||
}
|
||||
is Result.Failure -> store.dispatch(WalletAction.LoadPayId.Failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.CreatePayId.CompleteCreatingPayId -> {
|
||||
scope.launch {
|
||||
val cardId = store.state.globalState.card?.cardId
|
||||
val wallet = store.state.walletState.wallet
|
||||
val publicKey = store.state.globalState.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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -39,7 +104,10 @@ val walletMiddleware: Middleware<AppState> = { dispatch, state ->
|
|||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
withContext(Dispatchers.Main) {
|
||||
store.dispatch(WalletAction.LoadWallet(result.data.walletManager))
|
||||
store.dispatch(GlobalAction.LoadCard(result.data.card))
|
||||
store.dispatch(GlobalAction.LoadWalletManager(result.data.walletManager))
|
||||
store.dispatch(WalletAction.LoadWallet)
|
||||
store.dispatch(WalletAction.LoadPayId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.features.wallet.redux
|
||||
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
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.features.wallet.ui.BalanceStatus
|
||||
|
|
@ -16,24 +17,28 @@ fun walletReducer(action: Action, state: AppState): WalletState {
|
|||
|
||||
when (action) {
|
||||
is WalletAction.LoadWallet -> newState = WalletState(
|
||||
state = ProgressState.Loading, walletManager = action.walletManager,
|
||||
state = ProgressState.Loading,
|
||||
currencyData = BalanceWidgetData(
|
||||
BalanceStatus.Loading, action.walletManager.wallet.blockchain.fullName
|
||||
BalanceStatus.Loading,
|
||||
state.globalState.walletManager?.wallet?.blockchain?.fullName
|
||||
)
|
||||
)
|
||||
is WalletAction.LoadWallet.Success -> {
|
||||
val token = action.wallet.amounts[AmountType.Token]
|
||||
val tokenData = if (token != null) {
|
||||
TokenData(token.value.toString(), token.currencySymbol)
|
||||
TokenData(
|
||||
token.value?.toFormattedString(action.wallet.blockchain) ?: "",
|
||||
token.currencySymbol)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val amount = action.wallet.amounts[AmountType.Coin]?.value
|
||||
newState = newState.copy(
|
||||
state = ProgressState.Done, wallet = action.wallet,
|
||||
currencyData = BalanceWidgetData(
|
||||
BalanceStatus.VerifiedOnline, action.wallet.blockchain.fullName,
|
||||
action.wallet.amounts[AmountType.Coin]?.value?.toString(),
|
||||
token = tokenData
|
||||
amount?.toFormattedString(action.wallet.blockchain),
|
||||
token = tokenData,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -47,7 +52,24 @@ fun walletReducer(action: Action, state: AppState): WalletState {
|
|||
is WalletAction.HideQrCode -> {
|
||||
newState = newState.copy(qrCode = null)
|
||||
}
|
||||
|
||||
is WalletAction.LoadPayId.Success -> newState = newState.copy(
|
||||
payIdData = PayIdData(PayIdState.Created, action.payId)
|
||||
)
|
||||
is WalletAction.LoadPayId.NotCreated -> newState = newState.copy(
|
||||
payIdData = PayIdData(PayIdState.NotCreated, null)
|
||||
)
|
||||
is WalletAction.CreatePayId, is WalletAction.CreatePayId.Failure ->
|
||||
newState = newState.copy(creatingPayIdState = CreatingPayIdState.EnterPayId)
|
||||
is WalletAction.CreatePayId.CompleteCreatingPayId -> newState = newState.copy(
|
||||
creatingPayIdState = CreatingPayIdState.Waiting
|
||||
)
|
||||
is WalletAction.CreatePayId.Success -> newState = newState.copy(
|
||||
payIdData = PayIdData(PayIdState.Created, action.payId),
|
||||
creatingPayIdState = null
|
||||
)
|
||||
is WalletAction.CreatePayId.Cancel -> newState = newState.copy(
|
||||
creatingPayIdState = null
|
||||
)
|
||||
}
|
||||
return newState
|
||||
}
|
||||
|
|
@ -2,20 +2,27 @@ package com.tangem.tap.features.wallet.redux
|
|||
|
||||
import android.graphics.Bitmap
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.features.wallet.ui.PayIdData
|
||||
import org.rekotlin.StateType
|
||||
|
||||
data class WalletState(
|
||||
val state: ProgressState = ProgressState.Done,
|
||||
val cardImage: Bitmap? = null,
|
||||
val walletManager: WalletManager? = null,
|
||||
val wallet: Wallet? = null,
|
||||
val currencyData: BalanceWidgetData = BalanceWidgetData(),
|
||||
val payIdData: PayIdData = PayIdData(),
|
||||
val qrCode: Bitmap? = null
|
||||
val qrCode: Bitmap? = null,
|
||||
val creatingPayIdState: CreatingPayIdState? = null
|
||||
) : StateType
|
||||
|
||||
|
||||
enum class ProgressState { Loading, Done, Error }
|
||||
enum class ProgressState { Loading, Done, Error }
|
||||
|
||||
enum class PayIdState { Disabled, Loading, NotCreated, Created, ErrorLoading }
|
||||
|
||||
data class PayIdData(
|
||||
val payIdState: PayIdState = PayIdState.Loading,
|
||||
val payId: String? = null
|
||||
)
|
||||
|
||||
enum class CreatingPayIdState { EnterPayId, Waiting }
|
||||
|
|
@ -15,11 +15,6 @@ enum class PayIdState {
|
|||
Error
|
||||
}
|
||||
|
||||
data class PayIdData(
|
||||
val address: String? = null,
|
||||
val state: PayIdState? = null
|
||||
)
|
||||
|
||||
enum class BalanceStatus {
|
||||
VerifiedOnline,
|
||||
Unreachable,
|
||||
|
|
@ -36,7 +31,8 @@ data class BalanceWidgetData(
|
|||
|
||||
data class TokenData(
|
||||
val amount: String,
|
||||
val tokenSymbol: String
|
||||
val tokenSymbol: String,
|
||||
val fiatAmount: String? = null
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -58,6 +54,7 @@ class BalanceWidget(
|
|||
BalanceStatus.VerifiedOnline -> {
|
||||
fragment.tv_currency.text = data.currency
|
||||
fragment.tv_amount.text = data.amount
|
||||
fragment.tv_fiat_amount.show()
|
||||
fragment.tv_fiat_amount.text = data.fiatAmount
|
||||
showStatus(R.id.tv_status_verified)
|
||||
|
||||
|
|
@ -65,6 +62,7 @@ class BalanceWidget(
|
|||
fragment.l_token.show()
|
||||
fragment.l_token.tv_token_symbol.text = data.token.tokenSymbol
|
||||
fragment.l_token.tv_token_amount.text = data.token.amount
|
||||
fragment.l_token.tv_token_fiat_amount.text = data.token.fiatAmount
|
||||
} else {
|
||||
fragment.l_token.hide()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
package com.tangem.tap.features.wallet.ui
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
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.notificationsHandler
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.dialog_create_payid.*
|
||||
|
||||
class PayIdDialog(context: Context) : Dialog(context) {
|
||||
|
||||
init {
|
||||
this.setContentView(R.layout.dialog_create_payid)
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
notificationsHandler?.replaceBaseLayout(this.cl_payid_dialog)
|
||||
this.setOnDismissListener {
|
||||
notificationsHandler?.returnBaseLayout()
|
||||
store.dispatch(WalletAction.CreatePayId.Cancel)
|
||||
}
|
||||
this.btn_create_payid?.setOnClickListener {
|
||||
if (this.et_payid.text.isNullOrBlank()) {
|
||||
store.dispatch(WalletAction.CreatePayId.EmptyField)
|
||||
} else {
|
||||
val payid = this.et_payid.text!!.toString() +
|
||||
this.context.getString(R.string.wallet_pay_id_address)
|
||||
store.dispatch(WalletAction.CreatePayId.CompleteCreatingPayId(payid))
|
||||
}
|
||||
}
|
||||
super.show()
|
||||
}
|
||||
|
||||
fun showProgress() {
|
||||
this.btn_create_payid?.visibility = View.INVISIBLE
|
||||
this.pb_create_payid?.show()
|
||||
}
|
||||
|
||||
fun stopProgress() {
|
||||
this.btn_create_payid?.show()
|
||||
this.pb_create_payid?.hide()
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ class QrDialog(context: Context) : Dialog(context) {
|
|||
this.setContentView(R.layout.dialog_qrcode)
|
||||
}
|
||||
|
||||
fun show(qrCode: Bitmap, shareUrl: String) {
|
||||
fun showQr(qrCode: Bitmap, shareUrl: String) {
|
||||
this.setOnDismissListener { store.dispatch(WalletAction.HideQrCode) }
|
||||
this.btn_done?.setOnClickListener { store.dispatch(WalletAction.HideQrCode) }
|
||||
|
||||
|
|
@ -22,5 +22,4 @@ class QrDialog(context: Context) : Dialog(context) {
|
|||
this.iv_qrcode?.setImageBitmap(qrCode)
|
||||
super.show()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -7,9 +7,8 @@ import androidx.fragment.app.Fragment
|
|||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.redux.PayIdState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.card_balance.*
|
||||
|
|
@ -19,7 +18,8 @@ import org.rekotlin.StoreSubscriber
|
|||
|
||||
class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<WalletState> {
|
||||
|
||||
var dialog: QrDialog? = null
|
||||
private var qrDialog: QrDialog? = null
|
||||
private var payIdDialog: PayIdDialog? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -69,10 +69,10 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
btn_show_qr.setOnClickListener { store.dispatch(WalletAction.ShowQrCode) }
|
||||
|
||||
if (state.qrCode != null && state.wallet?.shareUrl != null) {
|
||||
dialog = QrDialog(requireContext())
|
||||
dialog?.show(state.qrCode, state.wallet.shareUrl)
|
||||
qrDialog = QrDialog(requireContext())
|
||||
qrDialog?.showQr(state.qrCode, state.wallet.shareUrl)
|
||||
} else {
|
||||
dialog?.dismiss()
|
||||
qrDialog?.dismiss()
|
||||
}
|
||||
|
||||
when (state.state) {
|
||||
|
|
@ -89,24 +89,37 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
}
|
||||
}
|
||||
|
||||
when (state.payIdData.state) {
|
||||
PayIdState.Loading -> {
|
||||
group_payid.hide()
|
||||
}
|
||||
when (state.payIdData.payIdState) {
|
||||
PayIdState.Disabled, PayIdState.Loading -> group_payid.hide()
|
||||
PayIdState.NotCreated -> {
|
||||
group_payid.show()
|
||||
tv_create_payid.show()
|
||||
tv_create_payid.setOnClickListener { store.dispatch(WalletAction.CreatePayId) }
|
||||
tv_payid_address.hide()
|
||||
}
|
||||
PayIdState.Loaded -> {
|
||||
PayIdState.Created -> {
|
||||
group_payid.show()
|
||||
tv_create_payid.hide()
|
||||
tv_payid_address.show()
|
||||
tv_payid_address.text = state.payIdData.address
|
||||
tv_payid_address.text = state.payIdData.payId
|
||||
|
||||
}
|
||||
PayIdState.ErrorLoading -> {
|
||||
}
|
||||
PayIdState.Error -> group_payid.hide()
|
||||
}
|
||||
|
||||
when (state.creatingPayIdState) {
|
||||
CreatingPayIdState.EnterPayId -> {
|
||||
if (payIdDialog == null) payIdDialog = PayIdDialog(requireContext())
|
||||
payIdDialog?.show()
|
||||
payIdDialog?.stopProgress()
|
||||
}
|
||||
CreatingPayIdState.Waiting -> payIdDialog?.showProgress()
|
||||
null -> {
|
||||
payIdDialog?.dismiss()
|
||||
payIdDialog = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
49
app/src/main/java/com/tangem/tap/network/Retrofit.kt
Normal file
49
app/src/main/java/com/tangem/tap/network/Retrofit.kt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package com.tangem.tap.network
|
||||
|
||||
import com.squareup.moshi.FromJson
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.ToJson
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Converter
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import java.math.BigDecimal
|
||||
|
||||
fun createRetrofitInstance(
|
||||
baseUrl: String,
|
||||
interceptors: List<Interceptor> = emptyList(),
|
||||
): Retrofit {
|
||||
val okHttpBuilder = OkHttpClient.Builder()
|
||||
interceptors.forEach { okHttpBuilder.addInterceptor(it) }
|
||||
|
||||
if (BuildConfig.DEBUG) okHttpBuilder.addInterceptor(createHttpLoggingInterceptor())
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(baseUrl)
|
||||
.addConverterFactory(createMoshiConverterFactory())
|
||||
.client(okHttpBuilder.build())
|
||||
.build()
|
||||
}
|
||||
|
||||
fun createMoshiConverterFactory(): Converter.Factory = MoshiConverterFactory.create(createMoshi())
|
||||
|
||||
fun createMoshi(): Moshi = Moshi.Builder()
|
||||
.add(BigDecimalAdapter)
|
||||
.add(KotlinJsonAdapterFactory()).build()
|
||||
|
||||
private fun createHttpLoggingInterceptor(): HttpLoggingInterceptor {
|
||||
val logging = HttpLoggingInterceptor()
|
||||
logging.level = HttpLoggingInterceptor.Level.BODY
|
||||
return logging
|
||||
}
|
||||
|
||||
private object BigDecimalAdapter {
|
||||
@FromJson
|
||||
fun fromJson(string: String) = BigDecimal(string)
|
||||
|
||||
@ToJson
|
||||
fun toJson(value: BigDecimal) = value.toString()
|
||||
}
|
||||
26
app/src/main/java/com/tangem/tap/network/payid/PayIdApi.kt
Normal file
26
app/src/main/java/com/tangem/tap/network/payid/PayIdApi.kt
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.tap.network.payid
|
||||
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface PayIdApi {
|
||||
@GET(API_PAY_ID_TANGEM)
|
||||
suspend fun getPayId(
|
||||
@Query("cid") cardId: String,
|
||||
@Query("key") publicKey: String
|
||||
): PayIdResponse
|
||||
|
||||
@POST(API_PAY_ID_TANGEM)
|
||||
suspend fun setPayId(
|
||||
@Query("cid") cardId: String,
|
||||
@Query("key") publicKey: String,
|
||||
@Query("payid") payId: String,
|
||||
@Query("address") address: String,
|
||||
@Query("network") network: String
|
||||
): SetPayIdResponse
|
||||
|
||||
companion object {
|
||||
const val API_PAY_ID_TANGEM = "https://payid.tangem.com/"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tangem.tap.network.payid
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.commands.common.network.Result
|
||||
import com.tangem.commands.common.network.performRequest
|
||||
import com.tangem.tap.network.createRetrofitInstance
|
||||
import retrofit2.Retrofit
|
||||
|
||||
class PayIdService {
|
||||
|
||||
private val payIdApi: PayIdApi by lazy {
|
||||
provideRetrofit()
|
||||
.create(PayIdApi::class.java)
|
||||
}
|
||||
|
||||
suspend fun getPayId(cardId: String, publicKey: String): Result<PayIdResponse> {
|
||||
return performRequest { payIdApi.getPayId(cardId, publicKey) }
|
||||
}
|
||||
|
||||
suspend fun setPayId(
|
||||
cardId: String, publicKey: String, payId: String, address: String, network: String
|
||||
): Result<SetPayIdResponse> {
|
||||
return performRequest { payIdApi.setPayId(cardId, publicKey, payId, address, network) }
|
||||
}
|
||||
|
||||
private fun provideRetrofit(): Retrofit = createRetrofitInstance("https://tangem.com/")
|
||||
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PayIdResponse(
|
||||
val payId: String
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class SetPayIdResponse(
|
||||
val success: Boolean
|
||||
)
|
||||
106
app/src/main/res/layout/dialog_create_payid.xml
Normal file
106
app/src/main/res/layout/dialog_create_payid.xml
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/cl_payid_dialog"
|
||||
android:layout_width="280dp"
|
||||
android:layout_height="280dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="280dp"
|
||||
android:layout_height="280dp"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_dialog_create_payid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:text="@string/wallet_dialog_pay_id_button"
|
||||
android:textColor="@color/textGray"
|
||||
android:textSize="17sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/til_payid"
|
||||
android:layout_width="118dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:hint="@string/wallet_dialog_pay_id_hint"
|
||||
android:textColorHint="@color/darkGray1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_dialog_create_payid">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/et_payid"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:inputType="textNoSuggestions|textVisiblePassword"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="4dp" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="12dp"
|
||||
android:text="@string/wallet_pay_id_address"
|
||||
android:textColor="@color/darkGray4"
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/til_payid"
|
||||
app:layout_constraintStart_toEndOf="@id/til_payid"
|
||||
app:layout_constraintTop_toTopOf="@id/til_payid" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:text="@string/wallet_dialog_pay_id_description"
|
||||
android:textColor="@color/darkGray4"
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/til_payid" />
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_create_payid"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:insetTop="0dp"
|
||||
android:insetBottom="0dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:text="@string/wallet_dialog_pay_id_button"
|
||||
android:textColor="@color/blue"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pb_create_payid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:textColor="@color/blue"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@id/btn_create_payid"
|
||||
app:layout_constraintStart_toStartOf="@id/btn_create_payid" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
@ -16,13 +16,11 @@
|
|||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:background="@android:color/white"
|
||||
android:elevation="3dp"
|
||||
android:padding="16dp">
|
||||
android:elevation="3dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="16dp">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_address"
|
||||
|
|
@ -49,7 +47,7 @@
|
|||
android:paddingStart="16dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingBottom="30dp"
|
||||
android:text="@string/wallet_explore_address"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="12sp"
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
android:paddingStart="16dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:textColor="@color/accent"
|
||||
android:textSize="12sp"
|
||||
app:drawableStartCompat="@drawable/ic_ok"
|
||||
|
|
@ -46,7 +45,6 @@
|
|||
android:paddingStart="16dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:textColor="@color/warning"
|
||||
android:textSize="12sp"
|
||||
app:drawableStartCompat="@drawable/ic_baseline_error_outline_24"
|
||||
|
|
@ -63,7 +61,6 @@
|
|||
android:paddingStart="16dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
<color name="backgroundGray">#F9F9FA</color>
|
||||
<color name="buttonGray">#F4F5F6</color>
|
||||
<color name="textGray">#DE000000</color>
|
||||
|
||||
|
||||
<color name="lightGray1">#F8F8FB</color>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,16 @@
|
|||
<string name="wallet_account_not_created">Account is not created</string>
|
||||
<string name="wallet_tokens">Tokens</string>
|
||||
|
||||
<string name="wallet_dialog_pay_id_button">Create PayID</string>
|
||||
<string name="wallet_create_payid_empty">Enter desired PayID first</string>
|
||||
<string name="wallet_dialog_pay_id_hint">PayID name</string>
|
||||
<string name="wallet_pay_id_address" translatable="false">$payid.tangem.com</string>
|
||||
<string name="wallet_dialog_pay_id_description">Your PayID is information unique to you, like your phone number, email or ABN.</string>
|
||||
|
||||
|
||||
<string name="error_payid_already_created">This PayID already exists. Try a different one.</string>
|
||||
<string name="error_creating_payid">Error response while creating PayID.</string>
|
||||
|
||||
|
||||
<string name="send_address_or_payid">Address or PayID</string>
|
||||
<string name="send_network_fee">Network fee</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue