Updated on 2026-08-14
This commit is contained in:
parent
842b3e494a
commit
e01127186c
14 changed files with 128 additions and 131 deletions
|
|
@ -3,19 +3,19 @@ package com.tangem.tap.common.log
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.orhanobut.logger.AndroidLogAdapter
|
import com.orhanobut.logger.AndroidLogAdapter
|
||||||
import com.orhanobut.logger.Logger
|
import com.orhanobut.logger.Logger
|
||||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
import com.tangem.datasource.local.logs.AppLogsStore
|
||||||
import com.tangem.wallet.BuildConfig
|
import com.tangem.wallet.BuildConfig
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tangem app logger
|
* Tangem app logger
|
||||||
*
|
*
|
||||||
* @property settingsRepository repository for saving logs
|
* @property appLogsStore app logs store
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
class TangemAppLoggerInitializer(
|
class TangemAppLoggerInitializer(
|
||||||
private val settingsRepository: SettingsRepository,
|
private val appLogsStore: AppLogsStore,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/** Initialize */
|
/** Initialize */
|
||||||
|
|
@ -35,7 +35,7 @@ class TangemAppLoggerInitializer(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PERMITTED_PRIORITY.contains(priority)) {
|
if (PERMITTED_PRIORITY.contains(priority)) {
|
||||||
settingsRepository.saveLogMessage(message)
|
appLogsStore.saveLogMessage(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,26 +3,26 @@ package com.tangem.tap.common.log
|
||||||
import com.tangem.Log
|
import com.tangem.Log
|
||||||
import com.tangem.LogFormat
|
import com.tangem.LogFormat
|
||||||
import com.tangem.TangemSdkLogger
|
import com.tangem.TangemSdkLogger
|
||||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
import com.tangem.datasource.local.logs.AppLogsStore
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CardSDK logger implementation
|
* CardSDK logger implementation
|
||||||
*
|
*
|
||||||
* @property levels logging levels
|
* @property levels logging levels
|
||||||
* @property messageFormatter message formatter
|
* @property messageFormatter message formatter
|
||||||
* @property settingsRepository settings repository
|
* @property appLogsStore app logs store
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
internal class TangemCardSDKLogger(
|
internal class TangemCardSDKLogger(
|
||||||
private val levels: List<Log.Level>,
|
private val levels: List<Log.Level>,
|
||||||
private val messageFormatter: LogFormat,
|
private val messageFormatter: LogFormat,
|
||||||
private val settingsRepository: SettingsRepository,
|
private val appLogsStore: AppLogsStore,
|
||||||
) : TangemSdkLogger {
|
) : TangemSdkLogger {
|
||||||
|
|
||||||
override fun log(message: () -> String, level: Log.Level) {
|
override fun log(message: () -> String, level: Log.Level) {
|
||||||
if (!levels.contains(level)) return
|
if (!levels.contains(level)) return
|
||||||
|
|
||||||
settingsRepository.saveLogMessage(message = messageFormatter.format(message, level))
|
appLogsStore.saveLogMessage(message = messageFormatter.format(message, level))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,7 +9,7 @@ import timber.log.Timber
|
||||||
val logMiddleware: Middleware<AppState> = { _, _ ->
|
val logMiddleware: Middleware<AppState> = { _, _ ->
|
||||||
{ nextDispatch ->
|
{ nextDispatch ->
|
||||||
{ action ->
|
{ action ->
|
||||||
Timber.i("Dispatch action: $action")
|
Timber.i("Dispatch action: ${action::class.java.simpleName}")
|
||||||
nextDispatch(action)
|
nextDispatch(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
package com.tangem.tap.data
|
package com.tangem.tap.data
|
||||||
|
|
||||||
import com.tangem.blockchain.common.logging.BlockchainSDKLogger
|
import com.tangem.blockchain.common.logging.BlockchainSDKLogger
|
||||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
import com.tangem.datasource.local.logs.AppLogsStore
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BlockchainSDK logger implementation
|
* BlockchainSDK logger implementation
|
||||||
*
|
*
|
||||||
* @property settingsRepository settings repository
|
* @property appLogsStore app logs store
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
internal class TangemBlockchainSDKLogger(
|
internal class TangemBlockchainSDKLogger(
|
||||||
private val settingsRepository: SettingsRepository,
|
private val appLogsStore: AppLogsStore,
|
||||||
) : BlockchainSDKLogger {
|
) : BlockchainSDKLogger {
|
||||||
|
|
||||||
override fun log(level: BlockchainSDKLogger.Level, message: String) {
|
override fun log(level: BlockchainSDKLogger.Level, message: String) {
|
||||||
settingsRepository.saveLogMessage(message)
|
appLogsStore.saveLogMessage(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ import com.tangem.Log
|
||||||
import com.tangem.LogFormat
|
import com.tangem.LogFormat
|
||||||
import com.tangem.TangemSdkLogger
|
import com.tangem.TangemSdkLogger
|
||||||
import com.tangem.blockchain.common.logging.BlockchainSDKLogger
|
import com.tangem.blockchain.common.logging.BlockchainSDKLogger
|
||||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
import com.tangem.datasource.local.logs.AppLogsStore
|
||||||
import com.tangem.tap.common.log.TangemAppLoggerInitializer
|
import com.tangem.tap.common.log.TangemAppLoggerInitializer
|
||||||
import com.tangem.tap.common.log.TangemCardSDKLogger
|
import com.tangem.tap.common.log.TangemCardSDKLogger
|
||||||
import com.tangem.tap.data.TangemBlockchainSDKLogger
|
import com.tangem.tap.data.TangemBlockchainSDKLogger
|
||||||
|
|
@ -20,13 +20,13 @@ internal object TangemLoggingModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideAppLoggerInitializer(settingsRepository: SettingsRepository): TangemAppLoggerInitializer {
|
fun provideAppLoggerInitializer(appLogsStore: AppLogsStore): TangemAppLoggerInitializer {
|
||||||
return TangemAppLoggerInitializer(settingsRepository)
|
return TangemAppLoggerInitializer(appLogsStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideCardSDKLogger(settingsRepository: SettingsRepository): TangemSdkLogger {
|
fun provideCardSDKLogger(appLogsStore: AppLogsStore): TangemSdkLogger {
|
||||||
val logLevels = listOf(
|
val logLevels = listOf(
|
||||||
Log.Level.ApduCommand,
|
Log.Level.ApduCommand,
|
||||||
Log.Level.Apdu,
|
Log.Level.Apdu,
|
||||||
|
|
@ -44,13 +44,13 @@ internal object TangemLoggingModule {
|
||||||
return TangemCardSDKLogger(
|
return TangemCardSDKLogger(
|
||||||
levels = logLevels,
|
levels = logLevels,
|
||||||
messageFormatter = LogFormat.StairsFormatter(),
|
messageFormatter = LogFormat.StairsFormatter(),
|
||||||
settingsRepository = settingsRepository,
|
appLogsStore = appLogsStore,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideBlockchainSDKLogger(settingsRepository: SettingsRepository): BlockchainSDKLogger {
|
fun provideBlockchainSDKLogger(appLogsStore: AppLogsStore): BlockchainSDKLogger {
|
||||||
return TangemBlockchainSDKLogger(settingsRepository)
|
return TangemBlockchainSDKLogger(appLogsStore)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,69 +1,109 @@
|
||||||
package com.tangem.datasource.local.logs
|
package com.tangem.datasource.local.logs
|
||||||
|
|
||||||
import androidx.datastore.preferences.core.MutablePreferences
|
import android.content.Context
|
||||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
|
||||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
|
import org.joda.time.format.DateTimeFormatterBuilder
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.BufferedWriter
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileWriter
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store for saving app logs
|
* Store for saving app logs
|
||||||
*
|
*
|
||||||
* @property appPreferencesStore app preferences store
|
* @property applicationContext application context
|
||||||
* @param dispatchers coroutine dispatcher provider
|
* @param dispatchers coroutine dispatcher provider
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
|
@Singleton
|
||||||
class AppLogsStore @Inject constructor(
|
class AppLogsStore @Inject constructor(
|
||||||
private val appPreferencesStore: AppPreferencesStore,
|
@ApplicationContext private val applicationContext: Context,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val scope = CoroutineScope(dispatchers.io)
|
private val scope = CoroutineScope(dispatchers.io)
|
||||||
private val mutex = Mutex()
|
private val mutex = Mutex()
|
||||||
|
|
||||||
|
private val file = File(applicationContext.filesDir, LOG_FILE_NAME)
|
||||||
|
|
||||||
|
private val formatter = DateTimeFormatterBuilder()
|
||||||
|
.appendDayOfMonth(2)
|
||||||
|
.appendLiteral('.')
|
||||||
|
.appendMonthOfYear(2)
|
||||||
|
.appendLiteral(' ')
|
||||||
|
.appendHourOfDay(1)
|
||||||
|
.appendLiteral(':')
|
||||||
|
.appendMinuteOfHour(2)
|
||||||
|
.appendLiteral(':')
|
||||||
|
.appendSecondOfMinute(2)
|
||||||
|
.appendLiteral('.')
|
||||||
|
.appendMillisOfSecond(3)
|
||||||
|
.toFormatter()
|
||||||
|
|
||||||
|
/** Get log file */
|
||||||
|
fun getFile(): File? = if (file.exists()) file else null
|
||||||
|
|
||||||
/** Save log [message] */
|
/** Save log [message] */
|
||||||
fun saveLogMessage(message: String) {
|
fun saveLogMessage(message: String) {
|
||||||
val newLogs = DateTime.now().millis.toString() to message
|
launchWithLock {
|
||||||
|
createFileIfNotExist()
|
||||||
|
|
||||||
appPreferencesStore.editDataWithLock { preferences ->
|
writeMessage(message)
|
||||||
val savedLogs = preferences.getObjectMap<String>(PreferencesKeys.APP_LOGS_KEY)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
preferences.setObjectMap(key = PreferencesKeys.APP_LOGS_KEY, value = savedLogs + newLogs)
|
/** Save log that consists from [messages] */
|
||||||
|
fun saveLogMessage(vararg messages: String) {
|
||||||
|
launchWithLock {
|
||||||
|
createFileIfNotExist()
|
||||||
|
|
||||||
|
writeMessage(*messages)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Delete deprecated logs if file size exceeds [maxSize] */
|
/** Delete deprecated logs if file size exceeds [maxSize] */
|
||||||
fun deleteDeprecatedLogs(maxSize: Int) {
|
fun deleteDeprecatedLogs(maxSize: Int) {
|
||||||
appPreferencesStore.editDataWithLock { preferences ->
|
launchWithLock {
|
||||||
val savedLogs = preferences.getObjectMap<String>(PreferencesKeys.APP_LOGS_KEY)
|
if (file.exists() && file.length() > maxSize) {
|
||||||
|
file.delete()
|
||||||
var sum = 0
|
|
||||||
preferences.setObjectMap(
|
|
||||||
key = PreferencesKeys.APP_LOGS_KEY,
|
|
||||||
value = savedLogs.entries
|
|
||||||
.sortedBy(Map.Entry<String, String>::key)
|
|
||||||
.takeLastWhile {
|
|
||||||
sum += it.value.length
|
|
||||||
sum < maxSize
|
|
||||||
}
|
|
||||||
.associate { it.key to it.value },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun AppPreferencesStore.editDataWithLock(
|
|
||||||
transform: suspend AppPreferencesStore.(MutablePreferences) -> Unit,
|
|
||||||
) {
|
|
||||||
scope.launch {
|
|
||||||
mutex.withLock {
|
|
||||||
editData(transform)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun writeMessage(vararg messages: String) {
|
||||||
|
BufferedWriter(FileWriter(file, true)).use { writer ->
|
||||||
|
writer.append(formatter.print(DateTime.now()))
|
||||||
|
writer.append(": ")
|
||||||
|
messages.forEach(writer::append)
|
||||||
|
writer.newLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createFileIfNotExist() {
|
||||||
|
if (!file.exists()) {
|
||||||
|
runCatching { file.createNewFile() }
|
||||||
|
.onFailure(Timber::e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun launchWithLock(callback: () -> Unit) {
|
||||||
|
scope.launch {
|
||||||
|
mutex.withLock {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val LOG_FILE_NAME = "logs.txt"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -54,8 +54,8 @@ internal class NetworkLogsSaveInterceptor(
|
||||||
val connectionProtocol = if (connection != null) " ${connection.protocol()}" else ""
|
val connectionProtocol = if (connection != null) " ${connection.protocol()}" else ""
|
||||||
|
|
||||||
appLogsStore.saveLogMessage(
|
appLogsStore.saveLogMessage(
|
||||||
"--> ${request.method} ${request.url}$connectionProtocol\n" +
|
"--> ${request.method} ${request.url}$connectionProtocol\n",
|
||||||
createRequestEndMessage(request),
|
createRequestEndMessage(request),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,12 +88,6 @@ internal class NetworkLogsSaveInterceptor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun logResponseMessage(response: Response, startNs: Long) {
|
private fun logResponseMessage(response: Response, startNs: Long) {
|
||||||
val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
|
|
||||||
|
|
||||||
val responseMessage = if (response.message.isEmpty()) "" else ' ' + response.message
|
|
||||||
val startMessage = "<-- ${response.code}$responseMessage ${response.request.url} " +
|
|
||||||
"(${tookMs}ms)"
|
|
||||||
|
|
||||||
val responseHeaders = response.headers
|
val responseHeaders = response.headers
|
||||||
val responseBody = response.body!!
|
val responseBody = response.body!!
|
||||||
val contentLength = responseBody.contentLength()
|
val contentLength = responseBody.contentLength()
|
||||||
|
|
@ -138,7 +132,17 @@ internal class NetworkLogsSaveInterceptor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
appLogsStore.saveLogMessage(startMessage + "\n" + message)
|
val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
|
||||||
|
|
||||||
|
val spaceBeforeResponseMessage = if (response.message.isEmpty()) "" else ' ' + response.message
|
||||||
|
|
||||||
|
appLogsStore.saveLogMessage(
|
||||||
|
"<-- ${response.code}",
|
||||||
|
spaceBeforeResponseMessage,
|
||||||
|
response.message,
|
||||||
|
" ${response.request.url} (${tookMs}ms)\n",
|
||||||
|
message,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bodyHasUnknownEncoding(headers: Headers): Boolean {
|
private fun bodyHasUnknownEncoding(headers: Headers): Boolean {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "LOCAL_USER_LOGS_ENABLED",
|
"name": "LOCAL_USER_LOGS_ENABLED",
|
||||||
"version": "undefined"
|
"version": "5.14.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "GENERATE_XPUB_ENABLED",
|
"name": "GENERATE_XPUB_ENABLED",
|
||||||
|
|
|
||||||
|
|
@ -6,48 +6,43 @@ import android.os.Build
|
||||||
import com.tangem.blockchain.common.Blockchain
|
import com.tangem.blockchain.common.Blockchain
|
||||||
import com.tangem.data.feedback.converters.BlockchainInfoConverter
|
import com.tangem.data.feedback.converters.BlockchainInfoConverter
|
||||||
import com.tangem.data.feedback.converters.CardInfoConverter
|
import com.tangem.data.feedback.converters.CardInfoConverter
|
||||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
import com.tangem.datasource.local.logs.AppLogsStore
|
||||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
|
||||||
import com.tangem.datasource.local.preferences.utils.getObjectMapSync
|
|
||||||
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
||||||
import com.tangem.domain.feedback.models.*
|
import com.tangem.domain.feedback.models.BlockchainErrorInfo
|
||||||
|
import com.tangem.domain.feedback.models.BlockchainInfo
|
||||||
|
import com.tangem.domain.feedback.models.PhoneInfo
|
||||||
|
import com.tangem.domain.feedback.models.UserWalletsInfo
|
||||||
import com.tangem.domain.feedback.repository.FeedbackRepository
|
import com.tangem.domain.feedback.repository.FeedbackRepository
|
||||||
import com.tangem.domain.models.scan.ScanResponse
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
|
||||||
import com.tangem.utils.coroutines.runCatching
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileWriter
|
|
||||||
import java.io.StringWriter
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of [FeedbackRepository]
|
* Implementation of [FeedbackRepository]
|
||||||
*
|
*
|
||||||
* @property appPreferencesStore application preferences store
|
* @property appLogsStore app logs store
|
||||||
* @property userWalletsListManager user wallets list manager
|
* @property userWalletsListManager user wallets list manager
|
||||||
* @property walletManagersStore wallet managers store
|
* @property walletManagersStore wallet managers store
|
||||||
* @property context context for getting app version
|
* @property context context for getting app version
|
||||||
* @property dispatchers coroutine dispatchers provider
|
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
internal class DefaultFeedbackRepository(
|
internal class DefaultFeedbackRepository(
|
||||||
private val appPreferencesStore: AppPreferencesStore,
|
private val appLogsStore: AppLogsStore,
|
||||||
private val userWalletsListManager: UserWalletsListManager,
|
private val userWalletsListManager: UserWalletsListManager,
|
||||||
private val walletManagersStore: WalletManagersStore,
|
private val walletManagersStore: WalletManagersStore,
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
|
||||||
) : FeedbackRepository {
|
) : FeedbackRepository {
|
||||||
|
|
||||||
private val blockchainsErrors = MutableStateFlow<Map<UserWalletId, BlockchainErrorInfo>>(emptyMap())
|
private val blockchainsErrors = MutableStateFlow<Map<UserWalletId, BlockchainErrorInfo>>(emptyMap())
|
||||||
|
|
||||||
override suspend fun getCardInfo(scanResponse: ScanResponse) = CardInfoConverter.convert(value = scanResponse)
|
override fun getCardInfo(scanResponse: ScanResponse) = CardInfoConverter.convert(value = scanResponse)
|
||||||
|
|
||||||
override suspend fun getUserWalletsInfo(userWalletId: UserWalletId?): UserWalletsInfo {
|
override fun getUserWalletsInfo(userWalletId: UserWalletId?): UserWalletsInfo {
|
||||||
return UserWalletsInfo(
|
return UserWalletsInfo(
|
||||||
selectedUserWalletId = userWalletId?.stringValue ?: "card isn't activated",
|
selectedUserWalletId = userWalletId?.stringValue ?: "card isn't activated",
|
||||||
totalUserWallets = userWalletsListManager.walletsCount,
|
totalUserWallets = userWalletsListManager.walletsCount,
|
||||||
|
|
@ -92,38 +87,13 @@ internal class DefaultFeedbackRepository(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getBlockchainErrorInfo(userWalletId: UserWalletId): BlockchainErrorInfo? {
|
override fun getBlockchainErrorInfo(userWalletId: UserWalletId): BlockchainErrorInfo? {
|
||||||
return blockchainsErrors.value[userWalletId].also {
|
return blockchainsErrors.value[userWalletId].also {
|
||||||
if (it == null) Timber.e("Blockchain error info is null for $userWalletId")
|
if (it == null) Timber.e("Blockchain error info is null for $userWalletId")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getAppLogs(): List<AppLogModel> {
|
override fun getLogFile(): File? = appLogsStore.getFile()
|
||||||
return appPreferencesStore.getObjectMapSync<String>(key = PreferencesKeys.APP_LOGS_KEY)
|
|
||||||
.map { AppLogModel(timestamp = it.key.toLong(), message = it.value) }
|
|
||||||
.sortedBy(AppLogModel::timestamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun createLogFile(logs: String): File? {
|
|
||||||
return runCatching(dispatchers.io) {
|
|
||||||
val file = File(context.filesDir, LOGS_FILE)
|
|
||||||
file.delete()
|
|
||||||
file.createNewFile()
|
|
||||||
|
|
||||||
val stringWriter = StringWriter()
|
|
||||||
|
|
||||||
stringWriter.append(logs)
|
|
||||||
|
|
||||||
val fileWriter = FileWriter(file)
|
|
||||||
fileWriter.write(stringWriter.toString())
|
|
||||||
fileWriter.close()
|
|
||||||
|
|
||||||
file
|
|
||||||
}.getOrElse {
|
|
||||||
Timber.e(it, "Logs file isn't created")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getAppVersion(): String {
|
private fun getAppVersion(): String {
|
||||||
return runCatching { context.packageManager.getPackageInfo(context.packageName, 0) }
|
return runCatching { context.packageManager.getPackageInfo(context.packageName, 0) }
|
||||||
|
|
@ -135,8 +105,4 @@ internal class DefaultFeedbackRepository(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
|
||||||
const val LOGS_FILE = "logs.txt"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,11 +2,10 @@ package com.tangem.data.feedback.di
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.tangem.data.feedback.DefaultFeedbackRepository
|
import com.tangem.data.feedback.DefaultFeedbackRepository
|
||||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
import com.tangem.datasource.local.logs.AppLogsStore
|
||||||
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
||||||
import com.tangem.domain.feedback.repository.FeedbackRepository
|
import com.tangem.domain.feedback.repository.FeedbackRepository
|
||||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
|
@ -21,18 +20,16 @@ internal object FeedbackRepositoryModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideFeedbackRepository(
|
fun provideFeedbackRepository(
|
||||||
appPreferencesStore: AppPreferencesStore,
|
appLogsStore: AppLogsStore,
|
||||||
userWalletsListManager: UserWalletsListManager,
|
userWalletsListManager: UserWalletsListManager,
|
||||||
walletManagersStore: WalletManagersStore,
|
walletManagersStore: WalletManagersStore,
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
|
||||||
): FeedbackRepository {
|
): FeedbackRepository {
|
||||||
return DefaultFeedbackRepository(
|
return DefaultFeedbackRepository(
|
||||||
appPreferencesStore = appPreferencesStore,
|
appLogsStore = appLogsStore,
|
||||||
userWalletsListManager = userWalletsListManager,
|
userWalletsListManager = userWalletsListManager,
|
||||||
walletManagersStore = walletManagersStore,
|
walletManagersStore = walletManagersStore,
|
||||||
context = context,
|
context = context,
|
||||||
dispatchers = dispatchers,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -37,10 +37,6 @@ internal class DefaultSettingsRepository(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveLogMessage(message: String) {
|
|
||||||
appLogsStore.saveLogMessage(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun deleteDeprecatedLogs(maxSize: Int) {
|
override fun deleteDeprecatedLogs(maxSize: Int) {
|
||||||
appLogsStore.deleteDeprecatedLogs(maxSize)
|
appLogsStore.deleteDeprecatedLogs(maxSize)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,11 @@ class GetFeedbackEmailUseCase(
|
||||||
private val emailMessageBodyResolver = EmailMessageBodyResolver(feedbackRepository)
|
private val emailMessageBodyResolver = EmailMessageBodyResolver(feedbackRepository)
|
||||||
|
|
||||||
suspend operator fun invoke(type: FeedbackEmailType): FeedbackEmail {
|
suspend operator fun invoke(type: FeedbackEmailType): FeedbackEmail {
|
||||||
val formattedLogs = AppLogsFormatter().format(appLogs = feedbackRepository.getAppLogs())
|
|
||||||
|
|
||||||
return FeedbackEmail(
|
return FeedbackEmail(
|
||||||
address = getAddress(type.cardInfo),
|
address = getAddress(type.cardInfo),
|
||||||
subject = emailSubjectResolver.resolve(type),
|
subject = emailSubjectResolver.resolve(type),
|
||||||
message = createMessage(type),
|
message = createMessage(type),
|
||||||
file = feedbackRepository.createLogFile(logs = formattedLogs),
|
file = feedbackRepository.getLogFile(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ import java.io.File
|
||||||
|
|
||||||
interface FeedbackRepository {
|
interface FeedbackRepository {
|
||||||
|
|
||||||
suspend fun getCardInfo(scanResponse: ScanResponse): CardInfo
|
fun getCardInfo(scanResponse: ScanResponse): CardInfo
|
||||||
|
|
||||||
suspend fun getUserWalletsInfo(userWalletId: UserWalletId?): UserWalletsInfo
|
fun getUserWalletsInfo(userWalletId: UserWalletId?): UserWalletsInfo
|
||||||
|
|
||||||
suspend fun getBlockchainInfoList(userWalletId: UserWalletId): List<BlockchainInfo>
|
suspend fun getBlockchainInfoList(userWalletId: UserWalletId): List<BlockchainInfo>
|
||||||
|
|
||||||
|
|
@ -23,9 +23,7 @@ interface FeedbackRepository {
|
||||||
|
|
||||||
fun saveBlockchainErrorInfo(error: BlockchainErrorInfo)
|
fun saveBlockchainErrorInfo(error: BlockchainErrorInfo)
|
||||||
|
|
||||||
suspend fun getBlockchainErrorInfo(userWalletId: UserWalletId): BlockchainErrorInfo?
|
fun getBlockchainErrorInfo(userWalletId: UserWalletId): BlockchainErrorInfo?
|
||||||
|
|
||||||
suspend fun getAppLogs(): List<AppLogModel>
|
fun getLogFile(): File?
|
||||||
|
|
||||||
suspend fun createLogFile(logs: String): File?
|
|
||||||
}
|
}
|
||||||
|
|
@ -10,8 +10,6 @@ interface SettingsRepository {
|
||||||
|
|
||||||
suspend fun setWalletScrollPreviewAvailability(isEnabled: Boolean)
|
suspend fun setWalletScrollPreviewAvailability(isEnabled: Boolean)
|
||||||
|
|
||||||
fun saveLogMessage(message: String)
|
|
||||||
|
|
||||||
fun deleteDeprecatedLogs(maxSize: Int)
|
fun deleteDeprecatedLogs(maxSize: Int)
|
||||||
|
|
||||||
suspend fun isSendTapHelpPreviewEnabled(): Boolean
|
suspend fun isSendTapHelpPreviewEnabled(): Boolean
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue