Updated on 2026-08-14
This commit is contained in:
commit
6e14ad22a8
13 changed files with 119 additions and 108 deletions
|
|
@ -64,7 +64,7 @@ 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.37.0'
|
||||
implementation 'com.tangem:blockchain:1.40.0'
|
||||
|
||||
//lifecycle
|
||||
implementation "androidx.lifecycle:lifecycle-runtime:2.2.0"
|
||||
|
|
@ -91,9 +91,12 @@ dependencies {
|
|||
// Camera
|
||||
implementation 'com.otaliastudios:cameraview:2.6.3'
|
||||
|
||||
// Image Loading - Picasso
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
|
||||
testImplementation 'junit:junit:4.13'
|
||||
testImplementation "com.google.truth:truth:1.0.1"
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap
|
||||
|
||||
import android.app.Application
|
||||
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.network.NetworkConnectivity
|
||||
|
|
@ -24,5 +25,6 @@ class TapApplication : Application() {
|
|||
}
|
||||
NetworkConnectivity.createInstance(store, this)
|
||||
preferencesStorage = PreferencesStorage(this)
|
||||
PicassoHelper.initPicassoWithCaching(this)
|
||||
}
|
||||
}
|
||||
39
app/src/main/java/com/tangem/tap/common/images/Picasso.kt
Normal file
39
app/src/main/java/com/tangem/tap/common/images/Picasso.kt
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.tap.common.images
|
||||
|
||||
import android.app.Application
|
||||
import com.squareup.picasso.OkHttp3Downloader
|
||||
import com.squareup.picasso.Picasso
|
||||
import okhttp3.Cache
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.OkHttpClient
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class PicassoHelper {
|
||||
companion object {
|
||||
private const val KEEP_CACHE_MAX_DAYS = 7
|
||||
|
||||
fun initPicassoWithCaching(application: Application) {
|
||||
val picasso = Picasso.Builder(application)
|
||||
.downloader(OkHttp3Downloader(getOkHttpForPicasso(application)))
|
||||
.build()
|
||||
Picasso.setSingletonInstance(picasso)
|
||||
}
|
||||
|
||||
private fun getOkHttpForPicasso(application: Application): OkHttpClient {
|
||||
val okHttpBuilder = OkHttpClient.Builder()
|
||||
okHttpBuilder.cache(Cache(File(application.filesDir, "artworks"), Long.MAX_VALUE))
|
||||
okHttpBuilder.addInterceptor { chain ->
|
||||
val cacheControl = CacheControl.Builder()
|
||||
.maxStale(KEEP_CACHE_MAX_DAYS, TimeUnit.DAYS)
|
||||
.build()
|
||||
val origRequest = chain.request()
|
||||
val neverExpireRequest = origRequest.newBuilder()
|
||||
.cacheControl(cacheControl)
|
||||
.build()
|
||||
chain.proceed(neverExpireRequest)
|
||||
}
|
||||
return okHttpBuilder.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,17 +2,14 @@ package com.tangem.tap.domain
|
|||
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.commands.Card
|
||||
import com.tangem.commands.CardStatus
|
||||
import com.tangem.commands.common.network.Result
|
||||
import com.tangem.commands.common.network.TangemService
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.tap.TapConfig
|
||||
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.tasks.ScanNoteResponse
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import com.tangem.tap.features.wallet.redux.PayIdState
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.network.NetworkConnectivity
|
||||
|
|
@ -26,7 +23,6 @@ import java.math.BigDecimal
|
|||
class TapWalletManager {
|
||||
private val payIdManager = PayIdManager()
|
||||
private val coinMarketCapService = CoinMarketCapService()
|
||||
private val tangemService = TangemService()
|
||||
|
||||
suspend fun loadWalletData() {
|
||||
val walletManager = store.state.globalState.scanNoteResponse?.walletManager
|
||||
|
|
@ -37,21 +33,6 @@ class TapWalletManager {
|
|||
updateWallet(walletManager)
|
||||
}
|
||||
|
||||
suspend fun loadArtwork(card: Card, artworkId: String) {
|
||||
val result =
|
||||
tangemService.getArtwork(card.cardId, card.cardPublicKey!!.toHexString(), artworkId)
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
store.dispatch(WalletAction.LoadArtwork.Success(
|
||||
Artwork(artworkId, result.data.byteStream().readBytes())
|
||||
))
|
||||
}
|
||||
is Result.Failure -> store.dispatch(WalletAction.LoadArtwork.Failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun loadPayId() {
|
||||
val result = loadPayIdIfNeeded()
|
||||
result?.let { handlePayIdResult(it) }
|
||||
|
|
|
|||
|
|
@ -2,17 +2,24 @@ package com.tangem.tap.domain.tasks
|
|||
|
||||
import com.tangem.CardSession
|
||||
import com.tangem.CardSessionRunnable
|
||||
import com.tangem.commands.CreateWalletCommand
|
||||
import com.tangem.commands.ReadCommand
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.tasks.CreateWalletTask
|
||||
|
||||
class CreateWalletAndRescanTask : CardSessionRunnable<ScanNoteResponse> {
|
||||
override val requiresPin2 = false
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<ScanNoteResponse>) -> Unit) {
|
||||
CreateWalletCommand().run(session) { result ->
|
||||
CreateWalletTask().run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> ScanNoteTask().run(session, callback)
|
||||
|
||||
is CompletionResult.Success -> {
|
||||
ReadCommand().run(session) { readResult ->
|
||||
when (readResult) {
|
||||
is CompletionResult.Success -> ScanNoteTask(readResult.data).run(session, callback)
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(readResult.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ data class ScanNoteResponse(
|
|||
val verifyResponse: VerifyCardResponse? = null
|
||||
) : CommandResponse
|
||||
|
||||
class ScanNoteTask : CardSessionRunnable<ScanNoteResponse> {
|
||||
class ScanNoteTask(val card: Card? = null) : CardSessionRunnable<ScanNoteResponse> {
|
||||
override val requiresPin2 = false
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<ScanNoteResponse>) -> Unit) {
|
||||
|
|
@ -27,7 +27,8 @@ class ScanNoteTask : CardSessionRunnable<ScanNoteResponse> {
|
|||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
|
||||
is CompletionResult.Success -> {
|
||||
val card = result.data
|
||||
val card = this.card ?: result.data
|
||||
|
||||
val walletManager = try {
|
||||
WalletManagerFactory.makeWalletManager(card)
|
||||
} catch (exception: Exception) {
|
||||
|
|
@ -49,14 +50,4 @@ class ScanNoteTask : CardSessionRunnable<ScanNoteResponse> {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Card?.toScanNoteCompletionResult(): CompletionResult<ScanNoteResponse> {
|
||||
this ?: return CompletionResult.Failure(TangemSdkError.CardError())
|
||||
val walletManager = try {
|
||||
WalletManagerFactory.makeWalletManager(this)
|
||||
} catch (exception: Exception) {
|
||||
return CompletionResult.Success(ScanNoteResponse(null, this))
|
||||
}
|
||||
return CompletionResult.Success(ScanNoteResponse(walletManager, this))
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ import com.tangem.tap.network.NetworkStateChanged
|
|||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -46,21 +45,6 @@ val walletMiddleware: Middleware<AppState> = { dispatch, state ->
|
|||
store.state.globalState.tapWalletManager.loadFiatRate(store.state.globalState.appCurrency)
|
||||
}
|
||||
}
|
||||
is WalletAction.LoadArtwork -> {
|
||||
if (action.artworkId != null) {
|
||||
scope.launch {
|
||||
store.state.globalState.tapWalletManager.loadArtwork(
|
||||
action.card, action.artworkId
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val artworkRes = R.drawable.card_default
|
||||
store.dispatch(WalletAction.LoadArtwork.Success(Artwork(
|
||||
artworkId = artworkRes.toString(), artworkResId = artworkRes
|
||||
)))
|
||||
}
|
||||
|
||||
}
|
||||
is WalletAction.CreateWallet -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.createWallet(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.features.wallet.redux
|
|||
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.tap.common.extensions.toFiatString
|
||||
import com.tangem.tap.common.extensions.toFormattedCurrencyString
|
||||
import com.tangem.tap.common.extensions.toFormattedString
|
||||
|
|
@ -12,6 +13,8 @@ import com.tangem.tap.features.wallet.models.toPendingTransactions
|
|||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.features.wallet.ui.TokenData
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.Action
|
||||
|
||||
class WalletReducer {
|
||||
|
|
@ -165,8 +168,18 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
token = newState.currencyData.token?.copy(fiatAmount = tokenFiatAmount)
|
||||
))
|
||||
}
|
||||
is WalletAction.LoadArtwork.Success -> {
|
||||
newState = newState.copy(cardImage = action.artwork)
|
||||
is WalletAction.LoadArtwork -> {
|
||||
// TODO: form URL in Card SDK
|
||||
var artworkAddress = "https://verify.tangem.com/card/artwork"
|
||||
artworkAddress += "?artworkId=${action.artworkId}"
|
||||
artworkAddress += "&CID=${store.state.globalState.scanNoteResponse?.card?.cardId}"
|
||||
artworkAddress += "&publicKey=${store.state.globalState.scanNoteResponse?.card?.cardPublicKey?.toHexString()}"
|
||||
val artwork = if (action.artworkId != null) {
|
||||
Artwork(artworkId = artworkAddress)
|
||||
} else {
|
||||
Artwork(artworkResId = R.drawable.card_default)
|
||||
}
|
||||
newState = newState.copy(cardImage = artwork)
|
||||
}
|
||||
is WalletAction.ShowQrCode -> {
|
||||
newState = newState.copy(
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@ data class WalletState(
|
|||
val payIdData: PayIdData = PayIdData(),
|
||||
val walletDialog: WalletDialog? = null,
|
||||
val mainButton: WalletMainButton = WalletMainButton.SendButton(false)
|
||||
) : StateType
|
||||
) : StateType {
|
||||
val showDetails: Boolean =
|
||||
currencyData.status != com.tangem.tap.features.wallet.ui.BalanceStatus.EmptyCard &&
|
||||
currencyData.status != com.tangem.tap.features.wallet.ui.BalanceStatus.UnknownBlockchain
|
||||
}
|
||||
|
||||
sealed class WalletDialog {
|
||||
data class QrDialog(
|
||||
|
|
@ -58,7 +62,7 @@ data class AddressData(
|
|||
)
|
||||
|
||||
data class Artwork(
|
||||
val artworkId: String,
|
||||
val artworkId: String? = null,
|
||||
val artwork: Bitmap? = null,
|
||||
val artworkResId: Int? = null
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ class BalanceWidget(
|
|||
fragment.tv_status_error_message.text = data.errorMessage
|
||||
|
||||
showStatus(R.id.group_error)
|
||||
fragment.tv_status_error_message.show(!data.errorMessage.isNullOrBlank())
|
||||
}
|
||||
BalanceStatus.EmptyCard -> {
|
||||
fragment.l_balance.hide()
|
||||
|
|
|
|||
|
|
@ -3,18 +3,17 @@ package com.tangem.tap.features.wallet.ui
|
|||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.Menu.NONE
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.animation.AlphaAnimation
|
||||
import android.view.animation.Animation
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.tap.common.extensions.getDrawable
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
|
|
@ -36,7 +35,6 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
|
||||
private var dialog: Dialog? = null
|
||||
private var snackbar: Snackbar? = null
|
||||
private var artworkId: String? = null
|
||||
|
||||
private lateinit var viewAdapter: PendingTransactionsAdapter
|
||||
|
||||
|
|
@ -93,6 +91,12 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
override fun newState(state: WalletState) {
|
||||
if (activity == null) return
|
||||
|
||||
if (!state.showDetails) {
|
||||
toolbar.menu.removeItem(R.id.details_menu)
|
||||
} else if (toolbar.menu.findItem(R.id.details_menu) == null) {
|
||||
toolbar.menu.add(R.menu.wallet, R.id.details_menu, NONE, R.string.details_toolbar_title)
|
||||
}
|
||||
|
||||
srl_wallet.setOnRefreshListener {
|
||||
store.dispatch(WalletAction.LoadData)
|
||||
}
|
||||
|
|
@ -182,25 +186,15 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
}
|
||||
|
||||
private fun setupCardImage(cardImage: Artwork?) {
|
||||
if (cardImage?.artwork != null) {
|
||||
if (cardImage.artworkId != this.artworkId) {
|
||||
prepareShowingCardImage(cardImage.artworkId)
|
||||
iv_card.setImageBitmap(cardImage.artwork)
|
||||
}
|
||||
} else if (cardImage?.artworkResId != null) {
|
||||
if (cardImage.artworkId != this.artworkId) {
|
||||
prepareShowingCardImage(cardImage.artworkId)
|
||||
iv_card.setImageDrawable(getDrawable(cardImage.artworkResId))
|
||||
}
|
||||
val address = cardImage?.artworkId ?: cardImage?.artworkResId
|
||||
val picassoRequest = when (address) {
|
||||
is String -> Picasso.get().load(address)
|
||||
is Int -> Picasso.get().load(address)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareShowingCardImage(artworkId: String) {
|
||||
this.artworkId = artworkId
|
||||
val fadeInAnimation: Animation = AlphaAnimation(0.0f, 1.0f)
|
||||
fadeInAnimation.duration = 400
|
||||
iv_card.startAnimation(fadeInAnimation)
|
||||
iv_card_tangem_logo.hide()
|
||||
picassoRequest?.placeholder(R.drawable.card_placeholder)
|
||||
?.error(R.drawable.card_placeholder)
|
||||
?.into(iv_card)
|
||||
}
|
||||
|
||||
private fun handleDialogs(walletDialog: WalletDialog?) {
|
||||
|
|
@ -255,7 +249,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.wallet, menu)
|
||||
if (store.state.walletState.showDetails) inflater.inflate(R.menu.wallet, menu)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,19 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle"
|
||||
>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
|
||||
<corners
|
||||
android:radius="12dp"
|
||||
/>
|
||||
<size
|
||||
android:height="174dp"
|
||||
android:width="328dp"/>
|
||||
<gradient
|
||||
android:startColor="#FFFFFF"
|
||||
android:endColor="#FFFFFF"
|
||||
/>
|
||||
|
||||
|
||||
</shape>
|
||||
<corners android:radius="12dp" />
|
||||
<size
|
||||
android:width="328dp"
|
||||
android:height="174dp" />
|
||||
<gradient
|
||||
android:endColor="#FFFFFF"
|
||||
android:startColor="#FFFFFF" />
|
||||
</shape>
|
||||
</item>
|
||||
<item
|
||||
android:drawable="@drawable/ic_tangem_pale"
|
||||
android:gravity="center">
|
||||
<size
|
||||
android:width="85dp"
|
||||
android:height="23dp" />
|
||||
</item>
|
||||
</layer-list>
|
||||
|
|
|
|||
|
|
@ -60,17 +60,6 @@
|
|||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/card_placeholder"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_card_tangem_logo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_tangem_pale"
|
||||
app:layout_constraintStart_toStartOf="@id/iv_card"
|
||||
app:layout_constraintEnd_toEndOf="@id/iv_card"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_card"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_card"
|
||||
android:elevation="6dp"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_pending_transaction"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue