Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-18 16:20:32 +08:00
commit 9ed5d9bfad
7 changed files with 65 additions and 29 deletions

View file

@ -168,7 +168,7 @@ internal class DefaultCardSdkProvider @Inject constructor(
secureStorage = secureStorage,
authenticationManager = authenticationManager,
keystoreManager = keystoreManager,
wordlist = Wordlist.getWordlist(activity),
wordlist = Wordlist.getWordlist(),
config = config.apply {
val apiConfig = apiConfigsManager.getEnvironmentConfig(id = ApiConfig.ID.TangemTech)
tangemApiBaseUrl = apiConfig.baseUrl

View file

@ -3,10 +3,13 @@ package com.tangem.datasource.asset.loader
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import com.squareup.moshi.adapter
import com.tangem.utils.coroutines.runCatching
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.datasource.asset.reader.AssetReader
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.runCatching
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@ -25,25 +28,44 @@ import javax.inject.Singleton
class AssetLoader @Inject constructor(
val assetReader: AssetReader,
@NetworkMoshi val moshi: Moshi,
val analyticsExceptionHandler: AnalyticsExceptionHandler,
val dispatchers: CoroutineDispatcherProvider,
) {
/** Load content [Content] of asset file [fileName] */
@Suppress("SuspendFunSwallowedCancellation")
@OptIn(ExperimentalStdlibApi::class)
suspend inline fun <reified Content> load(fileName: String): Content? = runCatching(dispatchers.io) {
val json = assetReader.read(fullFileName = "$fileName.json")
suspend inline fun <reified Content> load(fileName: String): Content? = withContext(dispatchers.io) {
val json = runCatching { assetReader.read(fullFileName = "$fileName.json") }.getOrNull()
moshi.adapter<Content>().fromJson(json)
runCatching {
moshi.adapter<Content>().fromJson(json)
}
.fold(
onSuccess = { parsedConfig ->
if (parsedConfig == null) {
sendException(
fileName = fileName,
isParsingSuccess = true,
json = json,
)
Timber.e(IllegalStateException("Parsed config [$fileName] is null"))
}
parsedConfig
},
onFailure = { throwable ->
sendException(
fileName = fileName,
isParsingSuccess = false,
json = json,
)
Timber.e(throwable, "Failed to load config [$fileName] from assets")
null
},
)
}
.fold(
onSuccess = { parsedConfig ->
if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config [$fileName] is null"))
parsedConfig
},
onFailure = { throwable ->
Timber.e(throwable, "Failed to load config [$fileName] from assets")
null
},
)
/** Load list [V] values of asset file [fileName] */
suspend inline fun <reified V> loadList(fileName: String): List<V> = runCatching(dispatchers.io) {
@ -84,4 +106,18 @@ class AssetLoader @Inject constructor(
emptyMap()
},
)
fun sendException(fileName: String, isParsingSuccess: Boolean, json: String?) {
analyticsExceptionHandler.sendException(
ExceptionAnalyticsEvent(
exception = IllegalStateException("Parsing config is failed"),
params = mapOf(
"filename" to fileName,
"isParsingSuccess" to isParsingSuccess.toString(),
"json_size" to json?.length.toString(),
"json" to json?.take(n = 30).toString(),
),
),
)
}
}

View file

@ -1,7 +1,6 @@
package com.tangem.datasource.asset.reader
import android.content.res.AssetManager
import java.io.BufferedReader
/**
* Implementation of asset file reader
@ -13,7 +12,8 @@ internal class AndroidAssetReader(
) : AssetReader {
override suspend fun read(fullFileName: String): String {
return assetManager.open(fullFileName).bufferedReader()
.use(BufferedReader::readText)
return assetManager.open(fullFileName, AssetManager.ACCESS_BUFFER).use { inputStream ->
inputStream.readBytes().toString(Charsets.UTF_8)
}
}
}

View file

@ -5,6 +5,7 @@ import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import com.squareup.moshi.adapter
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.datasource.api.express.models.response.Asset
import com.tangem.datasource.asset.reader.AssetReader
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
@ -23,9 +24,11 @@ class AssetLoaderTest {
private val assetReader = mockk<AssetReader>()
private val moshi = mockk<Moshi>()
private val analyticsExceptionHandler = mockk<AnalyticsExceptionHandler>()
private val assetLoader = AssetLoader(
assetReader = assetReader,
moshi = moshi,
analyticsExceptionHandler = analyticsExceptionHandler,
dispatchers = TestingCoroutineDispatcherProvider(),
)

View file

@ -18,7 +18,7 @@ internal class AndroidAssetReaderTest {
@Test
fun read_content() = runTest {
every { assetManager.open(FILE_NAME) } returns json.byteInputStream()
every { assetManager.open(FILE_NAME, AssetManager.ACCESS_BUFFER) } returns json.byteInputStream()
val actual = assetReader.read(fullFileName = FILE_NAME)
@ -28,7 +28,7 @@ internal class AndroidAssetReaderTest {
@Test
fun read_error() = runTest {
val exception = IOException("Error")
every { assetManager.open(FILE_NAME) } throws exception
every { assetManager.open(FILE_NAME, AssetManager.ACCESS_BUFFER) } throws exception
runCatching { assetReader.read(fullFileName = FILE_NAME) }
.onSuccess { throw IllegalStateException("Error should be thrown") }

View file

@ -1,6 +1,5 @@
package com.tangem.features.hotwallet.common.repository
import android.content.Context
import com.tangem.crypto.bip39.DefaultMnemonic
import com.tangem.crypto.bip39.EntropyLength
import com.tangem.crypto.bip39.Mnemonic
@ -8,15 +7,13 @@ import com.tangem.crypto.bip39.Wordlist
import com.tangem.features.hotwallet.MnemonicRepository
import com.tangem.features.hotwallet.MnemonicRepository.MnemonicType
import com.tangem.sdk.extensions.getWordlist
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
internal class DefaultMnemonicRepository @Inject constructor(
@ApplicationContext private val context: Context,
) : MnemonicRepository {
private val wordlist = Wordlist.getWordlist(context)
internal class DefaultMnemonicRepository @Inject constructor() : MnemonicRepository {
override val words: Set<String> = wordlist.words.toHashSet()
private val wordlist by lazy(LazyThreadSafetyMode.NONE) { Wordlist.getWordlist() }
override val words: Set<String> by lazy(LazyThreadSafetyMode.NONE) { wordlist.words.toHashSet() }
override fun generateMnemonic(type: MnemonicType): Mnemonic = DefaultMnemonic(
entropy = when (type) {

View file

@ -7,11 +7,11 @@
tangemBlockchainSdk = "releases-5.35-1449"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "releases-5.35-589"
tangemCardSdk = "releases-5.35-593"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
tangemVico = "2.0.0-alpha.25-tangem12"
#tangemVico = "0.0.1" # Keep it! - used for local builds ^
tangemHotSdk = "develop-539"
tangemHotSdk = "develop-547"
#tangemHotSdk = "0.0.1" # Keep it! - used for local builds ^