Updated on 2026-08-14

This commit is contained in:
Tangem 2024-08-13 15:31:16 +03:00
parent f748b8539d
commit 434c459728
13 changed files with 103 additions and 114 deletions

View file

@ -32,6 +32,7 @@ import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
import com.tangem.features.details.DetailsFeatureToggles import com.tangem.features.details.DetailsFeatureToggles
import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles
import com.tangem.tap.common.log.TangemAppLoggerInitializer
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository
import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles
import com.tangem.tap.proxy.AppStateHolder import com.tangem.tap.proxy.AppStateHolder
@ -116,4 +117,6 @@ interface ApplicationEntryPoint {
fun getAppRouter(): AppRouter fun getAppRouter(): AppRouter
fun getPushNotificationsFeatureToggles(): PushNotificationsFeatureToggles fun getPushNotificationsFeatureToggles(): PushNotificationsFeatureToggles
fun getTangemAppLogger(): TangemAppLoggerInitializer
} }

View file

@ -6,8 +6,6 @@ import android.content.pm.PackageManager
import coil.ImageLoader import coil.ImageLoader
import coil.ImageLoaderFactory import coil.ImageLoaderFactory
import com.chuckerteam.chucker.api.ChuckerInterceptor import com.chuckerteam.chucker.api.ChuckerInterceptor
import com.orhanobut.logger.AndroidLogAdapter
import com.orhanobut.logger.Logger
import com.tangem.Log import com.tangem.Log
import com.tangem.LogFormat import com.tangem.LogFormat
import com.tangem.TangemSdkLogger import com.tangem.TangemSdkLogger
@ -54,8 +52,8 @@ import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandle
import com.tangem.tap.common.feedback.AdditionalFeedbackInfo import com.tangem.tap.common.feedback.AdditionalFeedbackInfo
import com.tangem.tap.common.feedback.LegacyFeedbackManager import com.tangem.tap.common.feedback.LegacyFeedbackManager
import com.tangem.tap.common.images.createCoilImageLoader import com.tangem.tap.common.images.createCoilImageLoader
import com.tangem.tap.common.log.TangemAppLoggerInitializer
import com.tangem.tap.common.log.TangemLogCollector import com.tangem.tap.common.log.TangemLogCollector
import com.tangem.tap.common.log.TimberFormatStrategy
import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.appReducer import com.tangem.tap.common.redux.appReducer
import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.global.GlobalAction
@ -69,7 +67,6 @@ import com.tangem.wallet.BuildConfig
import dagger.hilt.EntryPoints import dagger.hilt.EntryPoints
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.rekotlin.Store import org.rekotlin.Store
import timber.log.Timber
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository as WalletConnect2Repository import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository as WalletConnect2Repository
lateinit var store: Store<AppState> lateinit var store: Store<AppState>
@ -191,6 +188,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
private val pushNotificationsFeatureToggles: PushNotificationsFeatureToggles private val pushNotificationsFeatureToggles: PushNotificationsFeatureToggles
get() = entryPoint.getPushNotificationsFeatureToggles() get() = entryPoint.getPushNotificationsFeatureToggles()
private val tangemAppLoggerInitializer: TangemAppLoggerInitializer
get() = entryPoint.getTangemAppLogger()
// endregion // endregion
override fun onCreate() { override fun onCreate() {
@ -202,16 +202,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
fun init() { fun init() {
store = createReduxStore() store = createReduxStore()
if (BuildConfig.LOG_ENABLED) { tangemAppLoggerInitializer.initialize()
Logger.addLogAdapter(AndroidLogAdapter(TimberFormatStrategy()))
Timber.plant(
object : Timber.DebugTree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
Logger.log(priority, tag, message, t)
}
},
)
}
foregroundActivityObserver = ForegroundActivityObserver() foregroundActivityObserver = ForegroundActivityObserver()
activityResultCaller = foregroundActivityObserver activityResultCaller = foregroundActivityObserver

View file

@ -0,0 +1,48 @@
package com.tangem.tap.common.log
import android.util.Log
import com.orhanobut.logger.AndroidLogAdapter
import com.orhanobut.logger.Logger
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.wallet.BuildConfig
import timber.log.Timber
/**
* Tangem app logger
*
* @property settingsRepository repository for saving logs
*
[REDACTED_AUTHOR]
*/
class TangemAppLoggerInitializer(
private val settingsRepository: SettingsRepository,
) {
/** Initialize */
fun initialize() {
if (IS_LOG_ENABLED) {
Logger.addLogAdapter(AndroidLogAdapter(TimberFormatStrategy()))
}
Timber.plant(tree = createTimberTree())
}
private fun createTimberTree(): Timber.Tree {
return object : Timber.DebugTree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
if (IS_LOG_ENABLED) {
Logger.log(priority, tag, message, t)
}
if (PERMITTED_PRIORITY.contains(priority)) {
settingsRepository.saveLogMessage(message)
}
}
}
}
private companion object {
val IS_LOG_ENABLED: Boolean = BuildConfig.LOG_ENABLED
val PERMITTED_PRIORITY = listOf(Log.ERROR, Log.INFO)
}
}

View file

@ -4,11 +4,6 @@ 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.domain.settings.repositories.SettingsRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
/** /**
* CardSDK logger implementation * CardSDK logger implementation
@ -16,7 +11,6 @@ import kotlinx.coroutines.sync.withLock
* @property levels logging levels * @property levels logging levels
* @property messageFormatter message formatter * @property messageFormatter message formatter
* @property settingsRepository settings repository * @property settingsRepository settings repository
* @property dispatchers coroutine dispatchers provider
* *
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
@ -24,19 +18,11 @@ 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 settingsRepository: SettingsRepository,
private val dispatchers: CoroutineDispatcherProvider,
) : TangemSdkLogger { ) : TangemSdkLogger {
private val scope = CoroutineScope(dispatchers.main)
private val mutex = Mutex()
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
scope.launch(dispatchers.main) { settingsRepository.saveLogMessage(message = messageFormatter.format(message, level))
mutex.withLock {
settingsRepository.updateAppLogs(message = messageFormatter.format(message, level))
}
}
} }
} }

View file

@ -2,33 +2,19 @@ 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.domain.settings.repositories.SettingsRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
/** /**
* BlockchainSDK logger implementation * BlockchainSDK logger implementation
* *
* @property settingsRepository settings repository * @property settingsRepository settings repository
* @property dispatchers coroutine dispatchers provider
* *
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
internal class TangemBlockchainSDKLogger( internal class TangemBlockchainSDKLogger(
private val settingsRepository: SettingsRepository, private val settingsRepository: SettingsRepository,
private val dispatchers: CoroutineDispatcherProvider,
) : BlockchainSDKLogger { ) : BlockchainSDKLogger {
private val scope = CoroutineScope(dispatchers.main)
private val mutex = Mutex()
override fun log(level: BlockchainSDKLogger.Level, message: String) { override fun log(level: BlockchainSDKLogger.Level, message: String) {
scope.launch(dispatchers.main) { settingsRepository.saveLogMessage(message)
mutex.withLock {
settingsRepository.updateAppLogs(message)
}
}
} }
} }

View file

@ -1,25 +0,0 @@
package com.tangem.tap.di.data
import com.tangem.blockchain.common.logging.BlockchainSDKLogger
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.tap.data.TangemBlockchainSDKLogger
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object BlockchainSDKLoggerModule {
@Provides
@Singleton
fun provideBlockchainSDKLogger(
settingsRepository: SettingsRepository,
dispatchers: CoroutineDispatcherProvider,
): BlockchainSDKLogger {
return TangemBlockchainSDKLogger(settingsRepository, dispatchers)
}
}

View file

@ -3,9 +3,11 @@ package com.tangem.tap.di.data
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.blockchain.common.logging.BlockchainSDKLogger
import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.tap.common.log.TangemAppLoggerInitializer
import com.tangem.tap.common.log.TangemCardSDKLogger import com.tangem.tap.common.log.TangemCardSDKLogger
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.tap.data.TangemBlockchainSDKLogger
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@ -14,14 +16,17 @@ import javax.inject.Singleton
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
internal object CardSDKLoggerModule { internal object TangemLoggingModule {
@Provides @Provides
@Singleton @Singleton
fun provideCardSDKLogger( fun provideAppLoggerInitializer(settingsRepository: SettingsRepository): TangemAppLoggerInitializer {
settingsRepository: SettingsRepository, return TangemAppLoggerInitializer(settingsRepository)
dispatchers: CoroutineDispatcherProvider, }
): TangemSdkLogger {
@Provides
@Singleton
fun provideCardSDKLogger(settingsRepository: SettingsRepository): TangemSdkLogger {
val logLevels = listOf( val logLevels = listOf(
Log.Level.ApduCommand, Log.Level.ApduCommand,
Log.Level.Apdu, Log.Level.Apdu,
@ -40,7 +45,12 @@ internal object CardSDKLoggerModule {
levels = logLevels, levels = logLevels,
messageFormatter = LogFormat.StairsFormatter(), messageFormatter = LogFormat.StairsFormatter(),
settingsRepository = settingsRepository, settingsRepository = settingsRepository,
dispatchers = dispatchers,
) )
} }
@Provides
@Singleton
fun provideBlockchainSDKLogger(settingsRepository: SettingsRepository): BlockchainSDKLogger {
return TangemBlockchainSDKLogger(settingsRepository)
}
} }

View file

@ -1,34 +1,16 @@
package com.tangem.datasource.api.common.config package com.tangem.datasource.api.common.config
import com.tangem.datasource.BuildConfig
/** /**
* TangemTech [ApiConfig] * TangemTech [ApiConfig]
* *
* @property currentEnvironment current api environment * @property currentEnvironment current api environment
*/ */
internal data class TangemTech( internal data class TangemTech(
override val currentEnvironment: ApiEnvironment = initializeCurrentEnvironment(), override val currentEnvironment: ApiEnvironment = ApiEnvironment.PROD,
) : ApiConfig(currentEnvironment) { ) : ApiConfig(currentEnvironment) {
override val environments: Map<ApiEnvironment, String> = mapOf( override val environments: Map<ApiEnvironment, String> = mapOf(
ApiEnvironment.DEV to "https://devapi.tangem-tech.com/v1/", ApiEnvironment.DEV to "https://devapi.tangem-tech.com/v1/",
ApiEnvironment.PROD to "https://api.tangem-tech.com/v1/", ApiEnvironment.PROD to "https://api.tangem-tech.com/v1/",
) )
private companion object {
fun initializeCurrentEnvironment(): ApiEnvironment {
return when (BuildConfig.BUILD_TYPE) {
DEBUG_BUILD_TYPE,
INTERNAL_BUILD_TYPE,
-> ApiEnvironment.DEV
MOCKED_BUILD_TYPE,
EXTERNAL_BUILD_TYPE,
RELEASE_BUILD_TYPE,
-> ApiEnvironment.PROD
else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]")
}
}
}
} }

View file

@ -61,16 +61,7 @@ internal class ProdApiConfigsManagerTest(private val model: Model) {
private fun createTangemTechModel(): Model { private fun createTangemTechModel(): Model {
return Model( return Model(
id = ApiConfig.ID.TangemTech, id = ApiConfig.ID.TangemTech,
expected = when (BuildConfig.BUILD_TYPE) { expected = "https://api.tangem-tech.com/v1/",
DEBUG_BUILD_TYPE,
INTERNAL_BUILD_TYPE,
-> "https://devapi.tangem-tech.com/v1/"
MOCKED_BUILD_TYPE,
EXTERNAL_BUILD_TYPE,
RELEASE_BUILD_TYPE,
-> "https://api.tangem-tech.com/v1/"
else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]")
},
) )
} }
} }

View file

@ -5,12 +5,21 @@ import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
import com.tangem.datasource.local.preferences.utils.store import com.tangem.datasource.local.preferences.utils.store
import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.joda.time.DateTime import org.joda.time.DateTime
internal class DefaultSettingsRepository( internal class DefaultSettingsRepository(
private val appPreferencesStore: AppPreferencesStore, private val appPreferencesStore: AppPreferencesStore,
dispatchers: CoroutineDispatcherProvider,
) : SettingsRepository { ) : SettingsRepository {
private val scope = CoroutineScope(dispatchers.io)
private val mutex = Mutex()
override suspend fun shouldShowSaveUserWalletScreen(): Boolean { override suspend fun shouldShowSaveUserWalletScreen(): Boolean {
return appPreferencesStore.getSyncOrDefault( return appPreferencesStore.getSyncOrDefault(
key = PreferencesKeys.SHOULD_SHOW_SAVE_USER_WALLET_SCREEN_KEY, key = PreferencesKeys.SHOULD_SHOW_SAVE_USER_WALLET_SCREEN_KEY,
@ -36,13 +45,17 @@ internal class DefaultSettingsRepository(
) )
} }
override suspend fun updateAppLogs(message: String) { override fun saveLogMessage(message: String) {
val newLogs = DateTime.now().millis.toString() to message val newLogs = DateTime.now().millis.toString() to message
appPreferencesStore.editData { preferences -> scope.launch {
val savedLogs = preferences.getObjectMap<String>(PreferencesKeys.APP_LOGS_KEY) mutex.withLock {
appPreferencesStore.editData { preferences ->
val savedLogs = preferences.getObjectMap<String>(PreferencesKeys.APP_LOGS_KEY)
preferences.setObjectMap(key = PreferencesKeys.APP_LOGS_KEY, value = savedLogs + newLogs) preferences.setObjectMap(key = PreferencesKeys.APP_LOGS_KEY, value = savedLogs + newLogs)
}
}
} }
} }

View file

@ -9,6 +9,7 @@ import com.tangem.domain.settings.repositories.AppRatingRepository
import com.tangem.domain.settings.repositories.PermissionRepository import com.tangem.domain.settings.repositories.PermissionRepository
import com.tangem.domain.settings.repositories.PromoSettingsRepository import com.tangem.domain.settings.repositories.PromoSettingsRepository
import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.settings.repositories.SettingsRepository
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,8 +22,11 @@ internal object SettingsDataModule {
@Provides @Provides
@Singleton @Singleton
fun provideSettingsRepository(appPreferencesStore: AppPreferencesStore): SettingsRepository { fun provideSettingsRepository(
return DefaultSettingsRepository(appPreferencesStore = appPreferencesStore) appPreferencesStore: AppPreferencesStore,
dispatchers: CoroutineDispatcherProvider,
): SettingsRepository {
return DefaultSettingsRepository(appPreferencesStore = appPreferencesStore, dispatchers = dispatchers)
} }
@Provides @Provides

View file

@ -16,8 +16,8 @@ internal class FeedbackDataBuilder {
fun addCardInfo(cardInfo: CardInfo) { fun addCardInfo(cardInfo: CardInfo) {
builder.appendKeyValue("Card ID", cardInfo.cardId) builder.appendKeyValue("Card ID", cardInfo.cardId)
builder.appendKeyValue("Firmware version", cardInfo.firmwareVersion) builder.appendKeyValue("Firmware version", cardInfo.firmwareVersion)
builder.appendKeyValue("Linked cards count:", cardInfo.cardsCount) builder.appendKeyValue("Linked cards count", cardInfo.cardsCount)
builder.appendKeyValue("Has seed phrase:", cardInfo.isImported.toString()) builder.appendKeyValue("Has seed phrase", cardInfo.isImported.toString())
builder.appendKeyValue("Card Blockchain", cardInfo.cardBlockchain) builder.appendKeyValue("Card Blockchain", cardInfo.cardBlockchain)
builder.appendSignedHashes(cardInfo.signedHashesList) builder.appendSignedHashes(cardInfo.signedHashesList)
} }

View file

@ -11,7 +11,7 @@ interface SettingsRepository {
suspend fun setWalletScrollPreviewAvailability(isEnabled: Boolean) suspend fun setWalletScrollPreviewAvailability(isEnabled: Boolean)
@Throws @Throws
suspend fun updateAppLogs(message: String) fun saveLogMessage(message: String)
@Throws @Throws
suspend fun deleteDeprecatedLogs(maxSize: Int) suspend fun deleteDeprecatedLogs(maxSize: Int)