Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-11 18:55:37 +03:00
parent 330ae73357
commit e7efe7b76a
4636 changed files with 234864 additions and 63507 deletions

1
data/settings/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,39 @@
import com.tangem.plugin.configuration.configurations.extension.kaptForObfuscatingVariants
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
alias(deps.plugins.kotlin.kapt)
alias(deps.plugins.hilt.android)
id("configuration")
}
android {
namespace = "com.tangem.data.settings"
}
dependencies {
implementation(projects.core.datasource)
implementation(projects.core.utils)
implementation(projects.domain.balanceHiding.models)
implementation(projects.domain.settings)
implementation(deps.androidx.datastore)
// region DI
implementation(deps.hilt.android)
kapt(deps.hilt.kapt)
// endregion
// region Others dependencies
implementation(deps.jodatime)
implementation(deps.kotlin.coroutines)
implementation(deps.moshi)
implementation(deps.moshi.kotlin)
implementation(deps.timber)
kaptForObfuscatingVariants(deps.moshi.kotlin.codegen)
kaptForObfuscatingVariants(deps.retrofit.response.type.keeper)
// endregion
}

View file

@ -0,0 +1,79 @@
package com.tangem.data.settings
import androidx.datastore.preferences.core.MutablePreferences
import androidx.datastore.preferences.core.edit
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.get
import com.tangem.domain.settings.repositories.AppRatingRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import java.util.Calendar
internal class DefaultAppRatingRepository(
private val appPreferencesStore: AppPreferencesStore,
) : AppRatingRepository {
override suspend fun setWalletWithFundsFound() {
appPreferencesStore.edit { mutablePreferences ->
val foundDate = mutablePreferences[PreferencesKeys.FUNDS_FOUND_DATE_KEY]
if (foundDate != null && foundDate != FUNDS_FOUND_DATE_UNDEFINED) return@edit
mutablePreferences[PreferencesKeys.FUNDS_FOUND_DATE_KEY] = Calendar.getInstance().timeInMillis
val appLaunchCount = mutablePreferences[PreferencesKeys.APP_LAUNCH_COUNT_KEY] ?: DEFAULT_APP_LAUNCH_COUNT
mutablePreferences[PreferencesKeys.SHOW_RATING_DIALOG_AT_LAUNCH_COUNT_KEY] =
appLaunchCount + FIRST_SHOWING_COUNT
}
}
override fun isReadyToShow(): Flow<Boolean> {
// TODO: [REDACTED_JIRA]
return combine(
appPreferencesStore.get(key = PreferencesKeys.USER_WAS_INTERACT_WITH_RATING_KEY, default = false),
appPreferencesStore.get(
key = PreferencesKeys.SHOW_RATING_DIALOG_AT_LAUNCH_COUNT_KEY,
default = FIRST_SHOWING_COUNT,
),
appPreferencesStore.get(key = PreferencesKeys.APP_LAUNCH_COUNT_KEY, default = DEFAULT_APP_LAUNCH_COUNT),
appPreferencesStore.get(key = PreferencesKeys.FUNDS_FOUND_DATE_KEY, default = FUNDS_FOUND_DATE_UNDEFINED),
) { isInteracting, ratingShowingCount, appLaunchCount, fundsFoundDate ->
if (!isInteracting) {
val diff = Calendar.getInstance().timeInMillis - fundsFoundDate
val diffInDays = diff / DAY_IN_MILLIS
val isFundsFound = fundsFoundDate != FUNDS_FOUND_DATE_UNDEFINED
appLaunchCount >= ratingShowingCount && diffInDays >= FIRST_SHOWING_COUNT && isFundsFound
} else {
appLaunchCount >= ratingShowingCount
}
}
}
override suspend fun remindLater() {
appPreferencesStore.edit { mutablePreferences ->
mutablePreferences[PreferencesKeys.APP_LAUNCH_COUNT_KEY]?.let {
mutablePreferences.updateNextShowing(it + DEFER_SHOWING_COUNT)
}
}
}
override suspend fun setNeverToShow() {
appPreferencesStore.edit { mutablePreferences ->
mutablePreferences.updateNextShowing(at = Int.MAX_VALUE)
}
}
private fun MutablePreferences.updateNextShowing(at: Int) {
this[PreferencesKeys.SHOW_RATING_DIALOG_AT_LAUNCH_COUNT_KEY] = at
this[PreferencesKeys.USER_WAS_INTERACT_WITH_RATING_KEY] = true
}
private companion object {
const val FUNDS_FOUND_DATE_UNDEFINED = -1L
const val DEFAULT_APP_LAUNCH_COUNT = 1
const val FIRST_SHOWING_COUNT = 3
const val DEFER_SHOWING_COUNT = 20
const val DAY_IN_MILLIS = 1000 * 60 * 60 * 24
}
}

View file

@ -0,0 +1,35 @@
package com.tangem.data.settings
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys.getShouldShowInitialPermissionScreen
import com.tangem.datasource.local.preferences.PreferencesKeys.getShouldShowPermission
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
import com.tangem.datasource.local.preferences.utils.store
import com.tangem.domain.settings.repositories.PermissionRepository
internal class DefaultPermissionRepository(
private val appPreferencesStore: AppPreferencesStore,
) : PermissionRepository {
override suspend fun shouldInitiallyShowPermissionScreen(permission: String): Boolean {
return appPreferencesStore.getSyncOrDefault(
key = getShouldShowInitialPermissionScreen(permission),
default = true,
)
}
override suspend fun neverInitiallyShowPermissionScreen(permission: String) {
appPreferencesStore.store(
key = getShouldShowInitialPermissionScreen(permission),
value = false,
)
}
override suspend fun shouldAskPermission(permission: String): Boolean {
return appPreferencesStore.getSyncOrDefault(getShouldShowPermission(permission), true)
}
override suspend fun neverAskPermission(permission: String) {
appPreferencesStore.store(key = getShouldShowPermission(permission), value = false)
}
}

View file

@ -0,0 +1,176 @@
package com.tangem.data.settings
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.GeoResponse
import com.tangem.datasource.local.logs.AppLogsStore
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
import com.tangem.datasource.local.preferences.utils.store
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.util.Locale
@Suppress("TooManyFunctions")
internal class DefaultSettingsRepository(
private val appPreferencesStore: AppPreferencesStore,
private val appLogsStore: AppLogsStore,
private val tangemTechApi: TangemTechApi,
private val dispatchers: CoroutineDispatcherProvider,
) : SettingsRepository {
private val userCountryFlow = MutableStateFlow<UserCountry?>(value = null)
override suspend fun shouldShowSaveUserWalletScreen(): Boolean {
return appPreferencesStore.getSyncOrDefault(
key = PreferencesKeys.SHOULD_SHOW_SAVE_USER_WALLET_SCREEN_KEY,
default = true,
)
}
override suspend fun setShouldShowSaveUserWalletScreen(value: Boolean) {
appPreferencesStore.store(key = PreferencesKeys.SHOULD_SHOW_SAVE_USER_WALLET_SCREEN_KEY, value = value)
}
override suspend fun isWalletScrollPreviewEnabled(): Boolean {
return appPreferencesStore.getSyncOrDefault(
key = PreferencesKeys.WALLETS_SCROLL_PREVIEW_KEY,
default = true,
)
}
override suspend fun setWalletScrollPreviewAvailability(isEnabled: Boolean) {
appPreferencesStore.store(
key = PreferencesKeys.WALLETS_SCROLL_PREVIEW_KEY,
value = isEnabled,
)
}
override fun deleteDeprecatedLogs(maxSize: Int) {
appLogsStore.deleteDeprecatedLogs(maxSize)
}
override suspend fun isSendTapHelpPreviewEnabled(): Boolean {
return appPreferencesStore.getSyncOrDefault(
key = PreferencesKeys.SEND_TAP_HELP_PREVIEW_KEY,
default = true,
)
}
override suspend fun setSendTapHelpPreviewAvailability(isEnabled: Boolean) {
appPreferencesStore.store(
key = PreferencesKeys.SEND_TAP_HELP_PREVIEW_KEY,
value = isEnabled,
)
}
override suspend fun wasApplicationStopped(): Boolean {
return appPreferencesStore.getSyncOrDefault(key = PreferencesKeys.WAS_APPLICATION_STOPPED_KEY, default = false)
}
override suspend fun setWasApplicationStopped(value: Boolean) {
appPreferencesStore.store(key = PreferencesKeys.WAS_APPLICATION_STOPPED_KEY, value = value)
}
override suspend fun shouldOpenWelcomeScreenOnResume(): Boolean {
return appPreferencesStore.getSyncOrDefault(
key = PreferencesKeys.SHOULD_OPEN_WELCOME_ON_RESUME_KEY,
default = false,
)
}
override suspend fun setShouldOpenWelcomeScreenOnResume(value: Boolean) {
appPreferencesStore.store(key = PreferencesKeys.SHOULD_OPEN_WELCOME_ON_RESUME_KEY, value = value)
}
override suspend fun shouldSaveAccessCodes(): Boolean {
return appPreferencesStore.getSyncOrDefault(key = PreferencesKeys.SHOULD_SAVE_ACCESS_CODES_KEY, default = false)
}
override suspend fun setShouldSaveAccessCodes(value: Boolean) {
appPreferencesStore.store(key = PreferencesKeys.SHOULD_SAVE_ACCESS_CODES_KEY, value = value)
}
override suspend fun incrementAppLaunchCounter() {
appPreferencesStore.editData { preferences ->
val count = preferences.getOrDefault(key = PreferencesKeys.APP_LAUNCH_COUNT_KEY, default = 0)
preferences[PreferencesKeys.APP_LAUNCH_COUNT_KEY] = count + 1
}
}
override suspend fun shouldShowMarketsTooltip(): Boolean {
return appPreferencesStore.getSyncOrDefault(
key = PreferencesKeys.SHOULD_SHOW_MARKETS_TOOLTIP_KEY,
default = true,
)
}
override suspend fun setMarketsTooltipShown(value: Boolean) {
appPreferencesStore.store(key = PreferencesKeys.SHOULD_SHOW_MARKETS_TOOLTIP_KEY, value = !value)
}
override suspend fun getUserCountryCodeSync(): UserCountry? {
// If user country code is already set, return it
val countryCode = userCountryFlow.value
if (countryCode != null) return countryCode
coroutineScope {
launch { fetchUserCountryCode() }
}
return null
}
override fun getUserCountryCode(): StateFlow<UserCountry?> = userCountryFlow
override suspend fun fetchUserCountryCode() {
Timber.i("Start fetching user country code")
withContext(dispatchers.io) {
val country = runCatching { tangemTechApi.getUserCountryCode() }
.fold(
onSuccess = GeoResponse::code,
onFailure = { Locale.getDefault().country },
)
.lowercase()
val code = when (country) {
UserCountry.Russia.code -> UserCountry.Russia
else -> UserCountry.Other(code = country)
}
Timber.i("User code country is $code")
userCountryFlow.value = code
}
}
override suspend fun setGoogleServicesAvailability(value: Boolean) {
appPreferencesStore.store(key = PreferencesKeys.IS_GOOGLE_SERVICES_AVAILABLE_KEY, value = value)
}
override suspend fun isGoogleServicesAvailability(): Boolean {
return appPreferencesStore.getSyncOrDefault(
key = PreferencesKeys.IS_GOOGLE_SERVICES_AVAILABLE_KEY,
default = false,
)
}
override suspend fun setGooglePayAvailability(value: Boolean) {
appPreferencesStore.store(key = PreferencesKeys.IS_GOOGLE_PAY_AVAILABLE_KEY, value = value)
}
override suspend fun isGooglePayAvailability(): Boolean {
return appPreferencesStore.getSyncOrDefault(
key = PreferencesKeys.IS_GOOGLE_PAY_AVAILABLE_KEY,
default = false,
)
}
}

View file

@ -0,0 +1,50 @@
package com.tangem.data.settings.di
import com.tangem.data.settings.DefaultAppRatingRepository
import com.tangem.data.settings.DefaultPermissionRepository
import com.tangem.data.settings.DefaultSettingsRepository
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.logs.AppLogsStore
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.settings.repositories.AppRatingRepository
import com.tangem.domain.settings.repositories.PermissionRepository
import com.tangem.domain.settings.repositories.SettingsRepository
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 SettingsDataModule {
@Provides
@Singleton
fun provideSettingsRepository(
appPreferencesStore: AppPreferencesStore,
appLogsStore: AppLogsStore,
tangemTechApi: TangemTechApi,
dispatchers: CoroutineDispatcherProvider,
): SettingsRepository {
return DefaultSettingsRepository(
appPreferencesStore = appPreferencesStore,
appLogsStore = appLogsStore,
tangemTechApi = tangemTechApi,
dispatchers = dispatchers,
)
}
@Provides
@Singleton
fun provideAppRatingRepository(appPreferencesStore: AppPreferencesStore): AppRatingRepository {
return DefaultAppRatingRepository(appPreferencesStore = appPreferencesStore)
}
@Provides
@Singleton
fun providePushPermissionRepository(appPreferencesStore: AppPreferencesStore): PermissionRepository {
return DefaultPermissionRepository(appPreferencesStore = appPreferencesStore)
}
}