Updated on 2026-08-14
This commit is contained in:
parent
ea780f563e
commit
42c8be234e
18 changed files with 275 additions and 193 deletions
|
|
@ -3,8 +3,10 @@ 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.datasource.asset.reader.AssetReader
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
|
@ -23,69 +25,63 @@ import javax.inject.Singleton
|
|||
class AssetLoader @Inject constructor(
|
||||
val assetReader: AssetReader,
|
||||
@NetworkMoshi val moshi: Moshi,
|
||||
val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
/** Load content [Content] of asset file [fileName] */
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
suspend inline fun <reified Content> load(fileName: String): Content? {
|
||||
return runCatching {
|
||||
val json = assetReader.read(fullFileName = "$fileName.json")
|
||||
suspend inline fun <reified Content> load(fileName: String): Content? = runCatching(dispatchers.io) {
|
||||
val json = assetReader.read(fullFileName = "$fileName.json")
|
||||
|
||||
moshi.adapter<Content>().fromJson(json)
|
||||
}
|
||||
.fold(
|
||||
onSuccess = { parsedConfig ->
|
||||
if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config [$fileName] is null"))
|
||||
parsedConfig
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Failed to load config [$fileName] from assets")
|
||||
null
|
||||
},
|
||||
)
|
||||
moshi.adapter<Content>().fromJson(json)
|
||||
}
|
||||
.fold(
|
||||
onSuccess = { parsedConfig ->
|
||||
if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config [$fileName] is null"))
|
||||
parsedConfig
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "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> {
|
||||
return runCatching {
|
||||
val json = assetReader.read(fullFileName = "$fileName.json")
|
||||
suspend inline fun <reified V> loadList(fileName: String): List<V> = runCatching(dispatchers.io) {
|
||||
val json = assetReader.read(fullFileName = "$fileName.json")
|
||||
|
||||
val type = Types.newParameterizedType(List::class.java, V::class.java)
|
||||
val adapter = moshi.adapter<List<V>>(type)
|
||||
val type = Types.newParameterizedType(List::class.java, V::class.java)
|
||||
val adapter = moshi.adapter<List<V>>(type)
|
||||
|
||||
adapter.fromJson(json)
|
||||
}
|
||||
.fold(
|
||||
onSuccess = { parsedConfig ->
|
||||
if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config [$fileName] is null"))
|
||||
parsedConfig.orEmpty()
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Failed to load config [$fileName] from assets")
|
||||
emptyList()
|
||||
},
|
||||
)
|
||||
adapter.fromJson(json)
|
||||
}
|
||||
.fold(
|
||||
onSuccess = { parsedConfig ->
|
||||
if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config [$fileName] is null"))
|
||||
parsedConfig.orEmpty()
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Failed to load config [$fileName] from assets")
|
||||
emptyList()
|
||||
},
|
||||
)
|
||||
|
||||
/** Load map [String] keys and [V] values of asset file [fileName] */
|
||||
suspend inline fun <reified V> loadMap(fileName: String): Map<String, V> {
|
||||
return runCatching {
|
||||
val json = assetReader.read(fullFileName = "$fileName.json")
|
||||
suspend inline fun <reified V> loadMap(fileName: String): Map<String, V> = runCatching(dispatchers.io) {
|
||||
val json = assetReader.read(fullFileName = "$fileName.json")
|
||||
|
||||
val type = Types.newParameterizedType(Map::class.java, String::class.java, V::class.java)
|
||||
val adapter = moshi.adapter<Map<String, V>>(type)
|
||||
val type = Types.newParameterizedType(Map::class.java, String::class.java, V::class.java)
|
||||
val adapter = moshi.adapter<Map<String, V>>(type)
|
||||
|
||||
adapter.fromJson(json)
|
||||
}
|
||||
.fold(
|
||||
onSuccess = { parsedConfig ->
|
||||
if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config [$fileName] is null"))
|
||||
parsedConfig.orEmpty()
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Failed to load config [$fileName] from assets")
|
||||
emptyMap()
|
||||
},
|
||||
)
|
||||
adapter.fromJson(json)
|
||||
}
|
||||
.fold(
|
||||
onSuccess = { parsedConfig ->
|
||||
if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config [$fileName] is null"))
|
||||
parsedConfig.orEmpty()
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Failed to load config [$fileName] from assets")
|
||||
emptyMap()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -1,23 +1,19 @@
|
|||
package com.tangem.datasource.asset.reader
|
||||
|
||||
import android.content.res.AssetManager
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.BufferedReader
|
||||
|
||||
/**
|
||||
* Implementation of asset file reader
|
||||
*
|
||||
* @property assetManager asset manager
|
||||
* @property dispatchers dispatchers
|
||||
*/
|
||||
internal class AndroidAssetReader(
|
||||
private val assetManager: AssetManager,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : AssetReader {
|
||||
|
||||
override suspend fun read(fullFileName: String): String = withContext(dispatchers.io) {
|
||||
assetManager.open(fullFileName).bufferedReader()
|
||||
override suspend fun read(fullFileName: String): String {
|
||||
return assetManager.open(fullFileName).bufferedReader()
|
||||
.use(BufferedReader::readText)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package com.tangem.datasource.di
|
|||
|
||||
import android.content.Context
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.datasource.local.*
|
||||
import com.tangem.datasource.local.preferences.*
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -26,6 +25,7 @@ internal object AppPreferencesStoreModule {
|
|||
return AppPreferencesStore(
|
||||
preferencesDataStore = PreferencesDataStore.getInstance(context = appContext, dispatcher = dispatchers.io),
|
||||
moshi = moshi,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ package com.tangem.datasource.di
|
|||
import android.content.Context
|
||||
import com.tangem.datasource.asset.reader.AndroidAssetReader
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -17,10 +16,7 @@ internal object AssetReaderModule {
|
|||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun providesAsserReader(
|
||||
@ApplicationContext context: Context,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): AssetReader {
|
||||
return AndroidAssetReader(context.assets, dispatchers)
|
||||
fun providesAsserReader(@ApplicationContext context: Context): AssetReader {
|
||||
return AndroidAssetReader(context.assets)
|
||||
}
|
||||
}
|
||||
|
|
@ -37,8 +37,12 @@ class AppLogsStore @Inject constructor(
|
|||
private val mutex = Mutex()
|
||||
private val zipMutex = Mutex()
|
||||
|
||||
private val file = File(applicationContext.filesDir, PERMITTED_FILE_NAME)
|
||||
private val fileZip = File(applicationContext.filesDir, PERMITTED_FILE_NAME_ZIP)
|
||||
private val logFile by lazy {
|
||||
File(applicationContext.filesDir, PERMITTED_FILE_NAME)
|
||||
}
|
||||
private val logFileZip by lazy {
|
||||
File(applicationContext.filesDir, PERMITTED_FILE_NAME_ZIP)
|
||||
}
|
||||
|
||||
private val formatter = DateTimeFormatterBuilder()
|
||||
.appendDayOfMonth(2)
|
||||
|
|
@ -55,12 +59,12 @@ class AppLogsStore @Inject constructor(
|
|||
.toFormatter()
|
||||
|
||||
/** Get log file */
|
||||
fun getFile(): File? = if (file.exists()) file else null
|
||||
fun getFile(): File? = if (logFile.exists()) logFile else null
|
||||
|
||||
suspend fun getZipFile(): File? {
|
||||
return zipMutex.withLock {
|
||||
if (file.exists()) {
|
||||
zip(listOf(file), fileZip)
|
||||
if (logFile.exists()) {
|
||||
zip(listOf(logFile), logFileZip)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
|
@ -98,8 +102,8 @@ class AppLogsStore @Inject constructor(
|
|||
/** Delete deprecated logs if file size exceeds [maxSize] */
|
||||
fun deleteDeprecatedLogs(maxSize: Int) {
|
||||
launchWithLock {
|
||||
if (file.exists() && file.length() > maxSize) {
|
||||
file.delete()
|
||||
if (logFile.exists() && logFile.length() > maxSize) {
|
||||
logFile.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -117,7 +121,7 @@ class AppLogsStore @Inject constructor(
|
|||
}
|
||||
|
||||
private fun writeMessage(tag: String, vararg messages: String) {
|
||||
BufferedWriter(FileWriter(file, true)).use { writer ->
|
||||
BufferedWriter(FileWriter(logFile, true)).use { writer ->
|
||||
writer.append(formatter.print(DateTime.now()))
|
||||
writer.append(": $tag ")
|
||||
messages.forEach(writer::append)
|
||||
|
|
@ -126,8 +130,8 @@ class AppLogsStore @Inject constructor(
|
|||
}
|
||||
|
||||
private fun createFileIfNotExist() {
|
||||
if (!file.exists()) {
|
||||
runCatching { file.createNewFile() }
|
||||
if (!logFile.exists()) {
|
||||
runCatching { logFile.createNewFile() }
|
||||
.onFailure(Timber::e)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import androidx.datastore.preferences.core.Preferences
|
|||
import androidx.datastore.preferences.core.edit
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
||||
/**
|
||||
* Application preferences store.
|
||||
|
|
@ -19,6 +20,7 @@ import com.squareup.moshi.Types
|
|||
*/
|
||||
class AppPreferencesStore(
|
||||
val moshi: Moshi,
|
||||
val dispatchers: CoroutineDispatcherProvider,
|
||||
private val preferencesDataStore: DataStore<Preferences>,
|
||||
) : DataStore<Preferences> by preferencesDataStore {
|
||||
|
||||
|
|
|
|||
|
|
@ -5,23 +5,25 @@ import androidx.datastore.preferences.core.edit
|
|||
import com.squareup.moshi.JsonDataException
|
||||
import com.squareup.moshi.Types
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/** Get flow of nullable data [T] by string [key] */
|
||||
inline fun <reified T> AppPreferencesStore.getObject(key: Preferences.Key<String>): Flow<T?> {
|
||||
val adapter = moshi.adapter(T::class.java)
|
||||
return data.map { preferences ->
|
||||
preferences[key]?.let {
|
||||
try {
|
||||
adapter.fromJson(it)
|
||||
} catch (e: JsonDataException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
return flow {
|
||||
val adapter = moshi.adapter(T::class.java)
|
||||
emitAll(
|
||||
data.map { preferences ->
|
||||
preferences[key]?.let {
|
||||
try {
|
||||
adapter.fromJson(it)
|
||||
} catch (e: JsonDataException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}.distinctUntilChanged(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -32,16 +34,19 @@ inline fun <reified T> AppPreferencesStore.getObject(key: Preferences.Key<String
|
|||
* @see getObjectList
|
||||
* */
|
||||
inline fun <reified T> AppPreferencesStore.getObject(key: Preferences.Key<String>, default: T): Flow<T> {
|
||||
val adapter = moshi.adapter(T::class.java) // TODO: Support parameterized types
|
||||
return data.map {
|
||||
try {
|
||||
it[key]?.let(adapter::fromJson) ?: default
|
||||
} catch (e: JsonDataException) {
|
||||
default
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
return flow {
|
||||
val adapter = moshi.adapter(T::class.java)
|
||||
emitAll(
|
||||
data.map {
|
||||
try {
|
||||
it[key]?.let(adapter::fromJson) ?: default
|
||||
} catch (e: JsonDataException) {
|
||||
default
|
||||
}
|
||||
}.distinctUntilChanged(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get nullable data [T] by string [key]
|
||||
*
|
||||
|
|
@ -49,26 +54,27 @@ inline fun <reified T> AppPreferencesStore.getObject(key: Preferences.Key<String
|
|||
*
|
||||
* @see getObjectListSync
|
||||
* */
|
||||
suspend inline fun <reified T> AppPreferencesStore.getObjectSyncOrNull(key: Preferences.Key<String>): T? {
|
||||
val adapter = moshi.adapter(T::class.java) // TODO: Support parameterized types
|
||||
return data.firstOrNull()
|
||||
?.get(key)
|
||||
?.let {
|
||||
try {
|
||||
adapter.fromJson(it)
|
||||
} catch (e: JsonDataException) {
|
||||
null
|
||||
suspend inline fun <reified T> AppPreferencesStore.getObjectSyncOrNull(key: Preferences.Key<String>): T? =
|
||||
withContext(dispatchers.io) {
|
||||
val adapter = moshi.adapter(T::class.java) // TODO: Support parameterized types
|
||||
data.firstOrNull()
|
||||
?.get(key)
|
||||
?.let {
|
||||
try {
|
||||
adapter.fromJson(it)
|
||||
} catch (e: JsonDataException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Get data [T] by string [key]. If data is not found, it returns [default] */
|
||||
suspend inline fun <reified T> AppPreferencesStore.getObjectSyncOrDefault(
|
||||
key: Preferences.Key<String>,
|
||||
default: T,
|
||||
): T {
|
||||
): T = withContext(dispatchers.io) {
|
||||
val adapter = moshi.adapter(T::class.java)
|
||||
return data.firstOrNull()
|
||||
data.firstOrNull()
|
||||
?.get(key)
|
||||
?.let {
|
||||
try {
|
||||
|
|
@ -87,37 +93,47 @@ suspend inline fun <reified T> AppPreferencesStore.getObjectSyncOrDefault(
|
|||
*
|
||||
* @see storeObjectList
|
||||
* */
|
||||
suspend inline fun <reified T> AppPreferencesStore.storeObject(key: Preferences.Key<String>, value: T) {
|
||||
val adapter = moshi.adapter(T::class.java) // TODO: Support parameterized types
|
||||
edit { it[key] = adapter.toJson(value) }
|
||||
}
|
||||
@Suppress("OptionalUnit")
|
||||
suspend inline fun <reified T> AppPreferencesStore.storeObject(key: Preferences.Key<String>, value: T): Unit =
|
||||
withContext(dispatchers.io) {
|
||||
val adapter = moshi.adapter(T::class.java) // TODO: Support parameterized types
|
||||
edit { it[key] = adapter.toJson(value) }
|
||||
}
|
||||
|
||||
/** Store list of data [value] by string [key] */
|
||||
suspend inline fun <reified T> AppPreferencesStore.storeObjectList(key: Preferences.Key<String>, value: List<T>) {
|
||||
val adapter = moshi.adapter<List<T>>(Types.newParameterizedType(List::class.java, T::class.java))
|
||||
edit { it[key] = adapter.toJson(value) }
|
||||
}
|
||||
suspend inline fun <reified T> AppPreferencesStore.storeObjectList(key: Preferences.Key<String>, value: List<T>) =
|
||||
withContext(dispatchers.io) {
|
||||
val adapter = moshi.adapter<List<T>>(Types.newParameterizedType(List::class.java, T::class.java))
|
||||
edit { it[key] = adapter.toJson(value) }
|
||||
}
|
||||
|
||||
/** Get flow of list of data [T] by string [key]. If data is not found, it returns `null` */
|
||||
inline fun <reified T> AppPreferencesStore.getObjectList(key: Preferences.Key<String>): Flow<List<T>?> {
|
||||
val adapter = moshi.adapter<List<T>>(Types.newParameterizedType(List::class.java, T::class.java))
|
||||
return data.map { it[key]?.let(adapter::fromJson) }.distinctUntilChanged()
|
||||
return flow {
|
||||
val adapter = moshi.adapter<List<T>>(Types.newParameterizedType(List::class.java, T::class.java))
|
||||
emitAll(
|
||||
data.map {
|
||||
it[key]?.let(adapter::fromJson)
|
||||
}.distinctUntilChanged(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Get list of data [T] by string [key], or empty if data is not found */
|
||||
suspend inline fun <reified T> AppPreferencesStore.getObjectListSync(key: Preferences.Key<String>): List<T> {
|
||||
val adapter = moshi.adapter<List<T>>(Types.newParameterizedType(List::class.java, T::class.java))
|
||||
return data.firstOrNull()
|
||||
?.get(key)
|
||||
?.let(adapter::fromJson)
|
||||
.orEmpty()
|
||||
}
|
||||
suspend inline fun <reified T> AppPreferencesStore.getObjectListSync(key: Preferences.Key<String>): List<T> =
|
||||
withContext(dispatchers.io) {
|
||||
val adapter = moshi.adapter<List<T>>(Types.newParameterizedType(List::class.java, T::class.java))
|
||||
data.firstOrNull()
|
||||
?.get(key)
|
||||
?.let(adapter::fromJson)
|
||||
.orEmpty()
|
||||
}
|
||||
|
||||
/** Store map with [String] key and value [V] by string [key] */
|
||||
suspend inline fun <reified V> AppPreferencesStore.storeObjectMap(
|
||||
key: Preferences.Key<String>,
|
||||
value: Map<String, V>,
|
||||
) {
|
||||
) = withContext(dispatchers.io) {
|
||||
val type = Types.newParameterizedType(Map::class.java, String::class.java, V::class.java)
|
||||
val adapter = moshi.adapter<Map<String, V>>(type)
|
||||
|
||||
|
|
@ -125,37 +141,47 @@ suspend inline fun <reified V> AppPreferencesStore.storeObjectMap(
|
|||
}
|
||||
|
||||
/** Get map with [String] key and value [V] by string [key], or empty if data is not found */
|
||||
suspend inline fun <reified V> AppPreferencesStore.getObjectMapSync(key: Preferences.Key<String>): Map<String, V> {
|
||||
val type = Types.newParameterizedType(Map::class.java, String::class.java, V::class.java)
|
||||
val adapter = moshi.adapter<Map<String, V>>(type)
|
||||
suspend inline fun <reified V> AppPreferencesStore.getObjectMapSync(key: Preferences.Key<String>): Map<String, V> =
|
||||
withContext(dispatchers.io) {
|
||||
val type = Types.newParameterizedType(Map::class.java, String::class.java, V::class.java)
|
||||
val adapter = moshi.adapter<Map<String, V>>(type)
|
||||
|
||||
return data.firstOrNull()
|
||||
?.get(key)
|
||||
?.let(adapter::fromJson)
|
||||
.orEmpty()
|
||||
}
|
||||
data.firstOrNull()
|
||||
?.get(key)
|
||||
?.let(adapter::fromJson)
|
||||
.orEmpty()
|
||||
}
|
||||
|
||||
/** Get flow of map with [String] key and value [V] by string [key], or empty if data is not found */
|
||||
inline fun <reified V> AppPreferencesStore.getObjectMap(key: Preferences.Key<String>): Flow<Map<String, V>> {
|
||||
val type = Types.newParameterizedType(Map::class.java, String::class.java, V::class.java)
|
||||
val adapter = moshi.adapter<Map<String, V>>(type)
|
||||
return flow {
|
||||
val type = Types.newParameterizedType(Map::class.java, String::class.java, V::class.java)
|
||||
val adapter = moshi.adapter<Map<String, V>>(type)
|
||||
|
||||
return data.map { it[key]?.let(adapter::fromJson) ?: emptyMap() }
|
||||
emitAll(
|
||||
data.map { it[key]?.let(adapter::fromJson) ?: emptyMap() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Get set of data [T] by string [key], or empty if data is not found */
|
||||
suspend inline fun <reified T> AppPreferencesStore.getObjectSetSync(key: Preferences.Key<String>): Set<T> {
|
||||
val adapter = moshi.adapter<Set<T>>(Types.newParameterizedType(Set::class.java, T::class.java))
|
||||
return data.firstOrNull()
|
||||
?.get(key)
|
||||
?.let(adapter::fromJson)
|
||||
.orEmpty()
|
||||
}
|
||||
suspend inline fun <reified T> AppPreferencesStore.getObjectSetSync(key: Preferences.Key<String>): Set<T> =
|
||||
withContext(dispatchers.io) {
|
||||
val adapter = moshi.adapter<Set<T>>(Types.newParameterizedType(Set::class.java, T::class.java))
|
||||
data.firstOrNull()
|
||||
?.get(key)
|
||||
?.let(adapter::fromJson)
|
||||
.orEmpty()
|
||||
}
|
||||
|
||||
/** Get flow of set of [T] by string [key], or empty if data is not found */
|
||||
inline fun <reified T> AppPreferencesStore.getObjectSet(key: Preferences.Key<String>): Flow<Set<T>> {
|
||||
val adapter = moshi.adapter<Set<T>>(Types.newParameterizedType(Set::class.java, T::class.java))
|
||||
return data.map {
|
||||
it[key]?.let(adapter::fromJson) ?: emptySet()
|
||||
return flow {
|
||||
val adapter = moshi.adapter<Set<T>>(Types.newParameterizedType(Set::class.java, T::class.java))
|
||||
emitAll(
|
||||
data.map {
|
||||
it[key]?.let(adapter::fromJson) ?: emptySet()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,9 @@ internal class SharedPreferencesKeyMigration(
|
|||
private val keyName: String,
|
||||
) : DataMigration<Preferences> {
|
||||
|
||||
private val legacyPrefs = context.getSharedPreferences(legacyPrefsName, Context.MODE_PRIVATE)
|
||||
private val legacyPrefs by lazy {
|
||||
context.getSharedPreferences(legacyPrefsName, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
override suspend fun cleanUp() {
|
||||
val sharedPrefsEditor = legacyPrefs.edit()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.squareup.moshi.Types
|
|||
import com.squareup.moshi.adapter
|
||||
import com.tangem.datasource.api.express.models.response.Asset
|
||||
import com.tangem.datasource.asset.reader.AssetReader
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerifyOrder
|
||||
import io.mockk.every
|
||||
|
|
@ -22,7 +23,11 @@ class AssetLoaderTest {
|
|||
|
||||
private val assetReader = mockk<AssetReader>()
|
||||
private val moshi = mockk<Moshi>()
|
||||
private val assetLoader = AssetLoader(assetReader = assetReader, moshi = moshi)
|
||||
private val assetLoader = AssetLoader(
|
||||
assetReader = assetReader,
|
||||
moshi = moshi,
|
||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun load() = runTest {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.datasource.asset.reader
|
|||
|
||||
import android.content.res.AssetManager
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
|
@ -15,7 +14,7 @@ import java.io.IOException
|
|||
internal class AndroidAssetReaderTest {
|
||||
|
||||
private val assetManager = mockk<AssetManager>()
|
||||
private val assetReader = AndroidAssetReader(assetManager, TestingCoroutineDispatcherProvider())
|
||||
private val assetReader = AndroidAssetReader(assetManager)
|
||||
|
||||
@Test
|
||||
fun read_content() = runTest {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue