Updated on 2026-08-14

This commit is contained in:
Tangem 2022-06-24 19:09:57 +04:00
commit 13b0b403b6
37 changed files with 392 additions and 414 deletions

View file

@ -62,6 +62,7 @@ fun Blockchain.getNetworkName(): String {
Blockchain.Ethereum, Blockchain.EthereumTestnet -> "ERC20"
Blockchain.BSC, Blockchain.BSCTestnet -> "BEP20"
Blockchain.Binance, Blockchain.BinanceTestnet -> "BEP2"
Blockchain.Tron, Blockchain.TronTestnet -> "TRC20"
else -> ""
}
}

View file

@ -1,135 +0,0 @@
package com.tangem.tap.common.extensions
import android.graphics.*
import android.widget.TextView
import androidx.constraintlayout.utils.widget.ImageFilterView
import com.squareup.picasso.Callback
import com.squareup.picasso.Picasso
import com.squareup.picasso.Transformation
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.domain.extensions.getCustomIconUrl
import com.tangem.tap.domain.tokens.getIconUrl
import com.tangem.wallet.R
fun Picasso.loadCurrenciesIcon(
imageView: ImageFilterView,
textView: TextView,
token: Token? = null,
blockchain: Blockchain,
) {
val url: String? = if (token != null) {
token.id?.let { getIconUrl(it) } ?: token.getCustomIconUrl()
?: IconsUtil.getTokenIconUri(blockchain, token)?.toString()
} else {
getIconUrl(blockchain.toNetworkId())
}
imageView.setImageDrawable(null)
imageView.colorFilter = null
textView.text = null
when {
token?.symbol == QCX -> {
this.load(R.drawable.ic_qcx)?.into(imageView)
}
token?.symbol == VOYR -> {
this.load(R.drawable.ic_voyr)?.into(imageView)
}
url != 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
}
})
}
else -> {
setOfflineCurrencyImage(imageView, textView, token, blockchain)
}
}
}
private const val QCX = "QCX"
private const val VOYR = "VOYRME"
private fun setOfflineCurrencyImage(
imageView: ImageFilterView,
textView: TextView,
token: Token?,
blockchain: Blockchain,
) {
when (token) {
null -> setBlockchainImage(imageView, textView, blockchain)
else -> setTokenImage(imageView, textView, token, blockchain)
}
}
private fun setBlockchainImage(
imageView: ImageFilterView,
textView: TextView,
blockchain: Blockchain,
) {
imageView.setImageResource(blockchain.getRoundIconRes())
imageView.colorFilter = null
if (blockchain.isTestnet()) imageView.saturation = 0f
textView.text = null
}
private fun setTokenImage(
imageView: ImageFilterView,
textView: TextView,
token: Token,
tokenBlockchain: Blockchain
) {
imageView.setImageResource(R.drawable.shape_circle)
if (tokenBlockchain.isTestnet()) {
imageView.saturation = 0f
} else {
imageView.setColorFilter(token.getColor())
}
textView.text = token.symbol.take(1)
}
private class RoundedCornersTransform : Transformation {
override fun transform(source: Bitmap): Bitmap {
val size = source.width.coerceAtMost(source.height)
val x = (source.width - size) / 2
val y = (source.height - size) / 2
val squaredBitmap = Bitmap.createBitmap(source, x, y, size, size)
if (squaredBitmap != source) source.recycle()
val paint = Paint().apply {
shader = BitmapShader(squaredBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
isAntiAlias = true
}
val rectF = RectF(0f, 0f, source.width.toFloat(), source.height.toFloat())
val radius = size / 8f
val bitmap = Bitmap.createBitmap(size, size, source.config)
Canvas(bitmap).drawRoundRect(rectF, radius, radius, paint)
squaredBitmap.recycle()
return bitmap
}
override fun key(): String = "rounded_corners"
}

View file

@ -53,8 +53,7 @@ fun BigDecimal.toFiatString(
fiatCurrencyName: String,
formatWithSpaces: Boolean = false
): String {
var fiatValue = rateValue.multiply(this)
fiatValue = fiatValue.setScale(2, RoundingMode.HALF_UP)
val fiatValue = rateValue.multiply(this)
return fiatValue.toFormattedFiatValue(fiatCurrencyName, formatWithSpaces)
}
@ -67,7 +66,8 @@ fun BigDecimal.toFormattedFiatValue(
fiatCurrencyName: String,
formatWithSpaces: Boolean = false
): String {
val fiatValue = if (formatWithSpaces) this.formatWithSpaces() else this
val fiatValue = this.setScale(2, RoundingMode.HALF_UP)
.let { if (formatWithSpaces) it.formatWithSpaces() else it }
return "${fiatValue} $fiatCurrencyName"
}

View file

@ -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
}

View 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) }
}
}
}

View file

@ -1,68 +0,0 @@
package com.tangem.tap.common.images
import android.app.Application
import com.squareup.picasso.OkHttp3Downloader
import com.squareup.picasso.Picasso
import com.tangem.tap.logConfig
import com.tangem.wallet.BuildConfig
import okhttp3.Cache
import okhttp3.CacheControl
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import timber.log.Timber
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.isLoggingEnabled = logIsEnabled()
picasso.setIndicatorsEnabled(BuildConfig.DEBUG)
Picasso.setSingletonInstance(picasso)
}
private fun getOkHttpForPicasso(application: Application): OkHttpClient {
return OkHttpClient.Builder().apply {
cache(Cache(File(application.filesDir, "artworks"), Long.MAX_VALUE))
callTimeout(15000, TimeUnit.MILLISECONDS)
if (logIsEnabled()) {
addDebugInterceptors(this)
}
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)
}
}.build()
}
private fun addDebugInterceptors(okHttpBuilder: OkHttpClient.Builder) {
val picassoInterceptor = HttpLoggingInterceptor(PicassoOkHttpLogger()).apply {
level = HttpLoggingInterceptor.Level.BODY
}
okHttpBuilder.addInterceptor(picassoInterceptor)
}
private fun logIsEnabled(): Boolean = BuildConfig.DEBUG && logConfig.picasso
}
}
private class PicassoOkHttpLogger : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
Timber.d(message)
}
}