Updated on 2026-08-14
This commit is contained in:
parent
0c31e7fa6a
commit
18bd8ed03b
9 changed files with 252 additions and 37 deletions
|
|
@ -118,8 +118,10 @@ dependencies {
|
|||
// Camera
|
||||
implementation 'com.otaliastudios:cameraview:2.6.3'
|
||||
|
||||
// Image Loading - Picasso
|
||||
// Image Loading
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
implementation 'io.coil-kt:coil:2.1.0'
|
||||
implementation 'io.coil-kt:coil-compose:2.1.0'
|
||||
|
||||
// Firebase
|
||||
implementation platform('com.google.firebase:firebase-bom:26.0.0')
|
||||
|
|
@ -172,9 +174,6 @@ dependencies {
|
|||
|
||||
implementation "com.github.skydoves:androidveil:1.1.2"
|
||||
|
||||
implementation("io.coil-kt:coil:2.0.0-rc01")
|
||||
implementation("io.coil-kt:coil-compose:2.0.0-rc01")
|
||||
|
||||
implementation 'com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.6'
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 985642c135606ba1f59fd739127aec03024da79b
|
||||
Subproject commit 7f058ec409b4eaaf8688ecd4bf944ef8d58d3564
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.tap
|
||||
|
||||
import android.app.Application
|
||||
import coil.ImageLoader
|
||||
import coil.ImageLoaderFactory
|
||||
import com.appsflyer.AppsFlyerLib
|
||||
import com.google.firebase.ktx.Firebase
|
||||
import com.google.firebase.remoteconfig.ktx.remoteConfig
|
||||
|
|
@ -10,7 +12,7 @@ import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
|
|||
import com.tangem.domain.DomainLayer
|
||||
import com.tangem.network.common.MoshiConverter
|
||||
import com.tangem.tap.common.analytics.GlobalAnalyticsHandler
|
||||
import com.tangem.tap.common.images.PicassoHelper
|
||||
import com.tangem.tap.common.images.createCoilImageLoader
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.appReducer
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -43,7 +45,7 @@ lateinit var currenciesRepository: CurrenciesRepository
|
|||
lateinit var walletConnectRepository: WalletConnectRepository
|
||||
lateinit var shopService: TangemShopService
|
||||
|
||||
class TapApplication : Application() {
|
||||
class TapApplication : Application(), ImageLoaderFactory {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
|
|
@ -61,7 +63,6 @@ class TapApplication : Application() {
|
|||
|
||||
NetworkConnectivity.createInstance(store, this)
|
||||
preferencesStorage = PreferencesStorage(this)
|
||||
PicassoHelper.initPicassoWithCaching(this)
|
||||
currenciesRepository = CurrenciesRepository(
|
||||
this, store.state.domainNetworks.tangemTechService
|
||||
)
|
||||
|
|
@ -75,6 +76,10 @@ class TapApplication : Application() {
|
|||
initAppsFlyer()
|
||||
}
|
||||
|
||||
override fun newImageLoader(): ImageLoader {
|
||||
return createCoilImageLoader(context = this)
|
||||
}
|
||||
|
||||
private fun loadConfigs() {
|
||||
val moshi = MoshiConverter.defaultMoshi()
|
||||
val localLoader = FeaturesLocalLoader(this, moshi)
|
||||
|
|
@ -108,11 +113,6 @@ class TapApplication : Application() {
|
|||
}
|
||||
|
||||
data class LogConfig(
|
||||
// disables both [internal, http]
|
||||
val picasso: Boolean = BuildConfig.DEBUG,
|
||||
val picassoInternal: Boolean = true,
|
||||
val picassoHttp: Boolean = true,
|
||||
|
||||
val coil: Boolean = BuildConfig.DEBUG,
|
||||
val storeAction: Boolean = BuildConfig.DEBUG,
|
||||
|
||||
)
|
||||
|
|
@ -1,6 +1,11 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.graphics.*
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapShader
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import android.graphics.Shader
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
import com.squareup.picasso.Callback
|
||||
|
|
@ -32,6 +37,28 @@ fun Picasso.loadCurrenciesIcon(
|
|||
imageView.colorFilter = null
|
||||
textView.text = null
|
||||
|
||||
if (token != null) {
|
||||
setTokenImage(imageView, textView, token, blockchain)
|
||||
}
|
||||
this.load(url)
|
||||
.transform(RoundedCornersTransform())
|
||||
.noPlaceholder()
|
||||
?.into(imageView,
|
||||
object : Callback {
|
||||
override fun onError(e: Exception?) {
|
||||
setOfflineCurrencyImage(imageView, textView, token, blockchain)
|
||||
}
|
||||
|
||||
override fun onSuccess() {
|
||||
if (token != null) {
|
||||
imageView.colorFilter = null
|
||||
textView.text = null
|
||||
}
|
||||
if (blockchain.isTestnet()) imageView.saturation = 0f
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
when {
|
||||
token?.symbol == QCX -> {
|
||||
this.load(R.drawable.ic_qcx)?.into(imageView)
|
||||
|
|
@ -59,7 +86,8 @@ fun Picasso.loadCurrenciesIcon(
|
|||
}
|
||||
if (blockchain.isTestnet()) imageView.saturation = 0f
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
setOfflineCurrencyImage(imageView, textView, token, blockchain)
|
||||
|
|
@ -70,36 +98,43 @@ fun Picasso.loadCurrenciesIcon(
|
|||
private const val QCX = "QCX"
|
||||
private const val VOYR = "VOYRME"
|
||||
|
||||
private fun setOfflineCurrencyImage(
|
||||
private fun Picasso.setOfflineCurrencyImage(
|
||||
imageView: ImageFilterView,
|
||||
textView: TextView,
|
||||
token: Token?,
|
||||
blockchain: Blockchain,
|
||||
) {
|
||||
when (token) {
|
||||
null -> setBlockchainImage(imageView, textView, blockchain)
|
||||
else -> setTokenImage(imageView, textView, token, blockchain)
|
||||
if (token == null) {
|
||||
setBlockchainImage(imageView, textView, blockchain)
|
||||
} else {
|
||||
setTokenImage(imageView, textView, token, blockchain)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setBlockchainImage(
|
||||
private fun Picasso.setBlockchainImage(
|
||||
imageView: ImageFilterView,
|
||||
textView: TextView,
|
||||
blockchain: Blockchain,
|
||||
) {
|
||||
imageView.setImageResource(blockchain.getRoundIconRes())
|
||||
this
|
||||
.load(blockchain.getRoundIconRes())
|
||||
.into(imageView)
|
||||
|
||||
imageView.colorFilter = null
|
||||
if (blockchain.isTestnet()) imageView.saturation = 0f
|
||||
textView.text = null
|
||||
}
|
||||
|
||||
private fun setTokenImage(
|
||||
private fun Picasso.setTokenImage(
|
||||
imageView: ImageFilterView,
|
||||
textView: TextView,
|
||||
token: Token,
|
||||
tokenBlockchain: Blockchain
|
||||
) {
|
||||
imageView.setImageResource(R.drawable.shape_circle)
|
||||
this
|
||||
.load(R.drawable.shape_circle)
|
||||
.into(imageView)
|
||||
|
||||
if (tokenBlockchain.isTestnet()) {
|
||||
imageView.saturation = 0f
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.graphics.luminance
|
||||
import androidx.core.graphics.toColorInt
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -9,8 +11,13 @@ import com.tangem.wallet.R
|
|||
fun Token.getColor(): Int {
|
||||
return try {
|
||||
("#" + this.contractAddress.subSequence(2..7).toString())
|
||||
.toColorInt()
|
||||
.toColorInt()
|
||||
} catch (exception: Exception) {
|
||||
R.color.lightGray4
|
||||
}
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
fun Token.getTextColor(): Int {
|
||||
return if (this.getColor().luminance > 0.5) Color.BLACK else Color.WHITE
|
||||
}
|
||||
50
app/src/main/java/com/tangem/tap/common/images/Coil.kt
Normal file
50
app/src/main/java/com/tangem/tap/common/images/Coil.kt
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package com.tangem.tap.common.images
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import coil.ImageLoader
|
||||
import coil.util.Logger
|
||||
import com.tangem.tap.logConfig
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import timber.log.Timber
|
||||
|
||||
private const val COIL_LOG_TAG = "COIL"
|
||||
|
||||
fun createCoilImageLoader(
|
||||
context: Context
|
||||
): ImageLoader {
|
||||
return ImageLoader.Builder(context)
|
||||
.apply {
|
||||
if (!logConfig.coil) return@apply
|
||||
|
||||
logger(CoilTimberLogger())
|
||||
okHttpClient {
|
||||
OkHttpClient.Builder()
|
||||
.addNetworkInterceptor(
|
||||
HttpLoggingInterceptor { message ->
|
||||
Timber.tag(COIL_LOG_TAG).d(message)
|
||||
}
|
||||
.apply {
|
||||
level = HttpLoggingInterceptor.Level.BODY
|
||||
}
|
||||
)
|
||||
.build()
|
||||
|
||||
}
|
||||
}
|
||||
.build()
|
||||
}
|
||||
|
||||
|
||||
private class CoilTimberLogger : Logger {
|
||||
|
||||
override var level: Int = Log.DEBUG
|
||||
|
||||
override fun log(tag: String, priority: Int, message: String?, throwable: Throwable?) {
|
||||
with(Timber.tag(COIL_LOG_TAG)) {
|
||||
throwable?.let { e -> e(e, message) }
|
||||
message?.let { msg -> d(msg) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
package com.tangem.tap.features.wallet.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.annotation.ColorRes
|
||||
|
|
@ -11,20 +15,32 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.transition.TransitionInflater
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.tangem.tangem_sdk_new.extensions.dpToPx
|
||||
import com.tangem.tap.common.SnackbarHandler
|
||||
import com.tangem.tap.common.TestActions
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.extensions.appendIfNotNull
|
||||
import com.tangem.tap.common.extensions.beginDelayedTransition
|
||||
import com.tangem.tap.common.extensions.fitChipsByGroupWidth
|
||||
import com.tangem.tap.common.extensions.getColor
|
||||
import com.tangem.tap.common.extensions.getString
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.extensions.toQrCode
|
||||
import com.tangem.tap.common.recyclerView.SpaceItemDecoration
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.onboarding.getQRReceiveMessage
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.ErrorType
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
|
||||
import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter
|
||||
import com.tangem.tap.features.wallet.ui.adapters.WalletDetailWarningMessagesAdapter
|
||||
import com.tangem.tap.features.wallet.ui.images.loadCurrencyIcon
|
||||
import com.tangem.tap.features.wallet.ui.test.TestWalletDetails
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -199,9 +215,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
}
|
||||
|
||||
private fun handleCurrencyIcon(wallet: WalletData) = with(binding.lWalletDetails.lBalance) {
|
||||
Picasso.get().loadCurrenciesIcon(
|
||||
imageView = ivCurrency,
|
||||
textView = tvTokenLetter,
|
||||
loadCurrencyIcon(
|
||||
currencyImageView = ivCurrency,
|
||||
currencyTextView = tvTokenLetter,
|
||||
blockchain = wallet.currency.blockchain,
|
||||
token = (wallet.currency as? Currency.Token)?.token
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,18 +6,17 @@ import androidx.core.view.isVisible
|
|||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.tap.common.extensions.getString
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.loadCurrenciesIcon
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.images.loadCurrencyIcon
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.ItemCurrencyWalletBinding
|
||||
|
|
@ -98,9 +97,9 @@ class WalletAdapter
|
|||
lContent.root.show()
|
||||
}
|
||||
|
||||
Picasso.get().loadCurrenciesIcon(
|
||||
imageView = ivCurrency,
|
||||
textView = tvTokenLetter,
|
||||
loadCurrencyIcon(
|
||||
currencyImageView = ivCurrency,
|
||||
currencyTextView = tvTokenLetter,
|
||||
token = (wallet.currency as? Currency.Token)?.token,
|
||||
blockchain = wallet.currency.blockchain,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
package com.tangem.tap.features.wallet.ui.images
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
import coil.imageLoader
|
||||
import coil.load
|
||||
import coil.request.ImageRequest
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.IconsUtil
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.tap.common.extensions.getColor
|
||||
import com.tangem.tap.common.extensions.getRoundIconRes
|
||||
import com.tangem.tap.common.extensions.getTextColor
|
||||
import com.tangem.tap.domain.extensions.getCustomIconUrl
|
||||
import com.tangem.tap.domain.tokens.getIconUrl
|
||||
import com.tangem.wallet.R
|
||||
|
||||
private const val QCX = "QCX"
|
||||
private const val VOYR = "VOYRME"
|
||||
|
||||
fun loadCurrencyIcon(
|
||||
currencyImageView: ImageFilterView,
|
||||
currencyTextView: TextView,
|
||||
token: Token?,
|
||||
blockchain: Blockchain,
|
||||
) {
|
||||
when {
|
||||
token == null -> currencyImageView.loadIcon(
|
||||
iconUrl = getIconUrl(blockchain.toNetworkId()),
|
||||
placeholderRes = blockchain.getRoundIconRes(),
|
||||
onStart = {
|
||||
if (blockchain.isTestnet()) {
|
||||
currencyImageView.saturation = 0f
|
||||
} else {
|
||||
currencyImageView.colorFilter = null
|
||||
}
|
||||
}
|
||||
)
|
||||
token.symbol == QCX -> currencyImageView.load(R.drawable.ic_qcx)
|
||||
token.symbol == VOYR -> currencyImageView.load(R.drawable.ic_voyr)
|
||||
else -> currencyImageView.loadIcon(
|
||||
iconUrl = getTokenIconUrl(token, blockchain),
|
||||
placeholderRes = R.drawable.shape_circle,
|
||||
onStart = {
|
||||
currencyTextView.text = token.symbol.take(1)
|
||||
currencyTextView.setTextColor(token.getTextColor())
|
||||
|
||||
if (blockchain.isTestnet()) {
|
||||
currencyImageView.saturation = 0f
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
currencyImageView.colorFilter = PorterDuffColorFilter(
|
||||
/* color = */
|
||||
token.getColor(),
|
||||
/* mode = */
|
||||
PorterDuff.Mode.SRC_ATOP,
|
||||
)
|
||||
},
|
||||
onSuccess = {
|
||||
if (!blockchain.isTestnet()) {
|
||||
currencyImageView.colorFilter = null
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun ImageView.loadIcon(
|
||||
iconUrl: String?,
|
||||
placeholderRes: Int,
|
||||
crossinline onStart: () -> Unit = {},
|
||||
crossinline onSuccess: () -> Unit = {},
|
||||
crossinline onError: () -> Unit = {},
|
||||
) {
|
||||
ImageRequest.Builder(context)
|
||||
.data(iconUrl)
|
||||
.placeholder(placeholderRes)
|
||||
.error(placeholderRes)
|
||||
.fallback(placeholderRes)
|
||||
.transformations(
|
||||
RoundedCornersTransformation(
|
||||
topLeft = 32f,
|
||||
topRight = 32f,
|
||||
bottomLeft = 32f,
|
||||
bottomRight = 32f
|
||||
)
|
||||
)
|
||||
.listener(
|
||||
onStart = { onStart() },
|
||||
onSuccess = { _, _ -> onSuccess() },
|
||||
onError = { _, _ -> onError() }
|
||||
)
|
||||
.target(imageView = this)
|
||||
.build()
|
||||
.also(context.imageLoader::enqueue)
|
||||
}
|
||||
|
||||
private fun getTokenIconUrl(token: Token, blockchain: Blockchain): String? {
|
||||
return token.id?.let(::getIconUrl)
|
||||
?: token.getCustomIconUrl()
|
||||
?: IconsUtil.getTokenIconUri(blockchain, token)
|
||||
?.toString()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue