Updated on 2026-08-14
This commit is contained in:
parent
aa03d39dc0
commit
49bd0090ff
18 changed files with 300 additions and 88 deletions
|
|
@ -13,6 +13,10 @@ android {
|
|||
namespace = "com.tangem.data.settings"
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(projects.core.datasource)
|
||||
|
|
@ -28,6 +32,11 @@ dependencies {
|
|||
kapt(deps.hilt.kapt)
|
||||
// endregion
|
||||
|
||||
// region Test
|
||||
testImplementation(projects.test.core)
|
||||
testRuntimeOnly(deps.test.junit5.engine)
|
||||
// endregion
|
||||
|
||||
// region Others dependencies
|
||||
implementation(deps.jodatime)
|
||||
implementation(deps.kotlin.coroutines)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
package com.tangem.data.settings
|
||||
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.utils.get
|
||||
import com.tangem.domain.settings.HotWalletRestrictionManager
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
/**
|
||||
* Development implementation of [HotWalletRestrictionManager].
|
||||
*
|
||||
* Reads and writes the restriction state from [AppPreferencesStore],
|
||||
* allowing testers to toggle it via the Tester Menu.
|
||||
* The preference [Flow] is converted to a [StateFlow] on construction,
|
||||
* so [isCreationEnabledSync] can be called from non-suspending contexts.
|
||||
* Defaults to `true` (restriction enabled) when no value is stored.
|
||||
*/
|
||||
internal class DevHotWalletRestrictionManager(
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
) : HotWalletRestrictionManager {
|
||||
|
||||
private val isCreationEnabledState: StateFlow<Boolean> =
|
||||
appPreferencesStore
|
||||
.get(key = IS_HOT_WALLET_CREATION_RESTRICTION_ENABLED_KEY, default = true)
|
||||
.stateIn(
|
||||
scope = CoroutineScope(dispatchers.io + SupervisorJob()),
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = true,
|
||||
)
|
||||
|
||||
override fun isCreationEnabled(): StateFlow<Boolean> = isCreationEnabledState
|
||||
|
||||
override fun isCreationEnabledSync(): Boolean = isCreationEnabledState.value
|
||||
|
||||
override suspend fun toggleCreationEnabled() {
|
||||
appPreferencesStore.editData { preferences ->
|
||||
val isEnabled = preferences.getOrDefault(
|
||||
key = IS_HOT_WALLET_CREATION_RESTRICTION_ENABLED_KEY,
|
||||
default = true,
|
||||
)
|
||||
preferences[IS_HOT_WALLET_CREATION_RESTRICTION_ENABLED_KEY] = !isEnabled
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val IS_HOT_WALLET_CREATION_RESTRICTION_ENABLED_KEY =
|
||||
booleanPreferencesKey(name = "isHotWalletCreationRestrictionEnabled")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.data.settings
|
||||
|
||||
import com.tangem.domain.settings.HotWalletRestrictionManager
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Production implementation of [HotWalletRestrictionManager].
|
||||
*
|
||||
* Hot wallet creation restriction is always enabled in production builds —
|
||||
* users must scan a physical Tangem card to add a wallet.
|
||||
* [toggleCreationEnabled] is a no-op since the restriction cannot be changed at runtime.
|
||||
*/
|
||||
internal class ProdHotWalletRestrictionManager : HotWalletRestrictionManager {
|
||||
|
||||
private val state: StateFlow<Boolean> = MutableStateFlow(true)
|
||||
|
||||
override fun isCreationEnabled(): StateFlow<Boolean> = state
|
||||
override fun isCreationEnabledSync(): Boolean = true
|
||||
override suspend fun toggleCreationEnabled() = Unit
|
||||
}
|
||||
|
|
@ -1,12 +1,16 @@
|
|||
package com.tangem.data.settings.di
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.data.settings.BuildConfig
|
||||
import com.tangem.data.settings.DefaultAppRatingRepository
|
||||
import com.tangem.data.settings.DefaultPermissionRepository
|
||||
import com.tangem.data.settings.DefaultSettingsRepository
|
||||
import com.tangem.data.settings.DevHotWalletRestrictionManager
|
||||
import com.tangem.data.settings.ProdHotWalletRestrictionManager
|
||||
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.HotWalletRestrictionManager
|
||||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
import com.tangem.domain.settings.repositories.PermissionRepository
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
|
|
@ -55,4 +59,17 @@ internal object SettingsDataModule {
|
|||
context = context,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideHotWalletRestrictionManager(
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): HotWalletRestrictionManager {
|
||||
return if (BuildConfig.TESTER_MENU_ENABLED) {
|
||||
DevHotWalletRestrictionManager(appPreferencesStore, dispatchers)
|
||||
} else {
|
||||
ProdHotWalletRestrictionManager()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
package com.tangem.data.settings
|
||||
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.utils.get
|
||||
import com.tangem.test.core.getEmittedValues
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class DevHotWalletRestrictionManagerTest {
|
||||
|
||||
private val appPreferencesStore = mockk<AppPreferencesStore>(relaxed = true)
|
||||
private val dispatchers: CoroutineDispatcherProvider = TestingCoroutineDispatcherProvider()
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
mockkStatic("com.tangem.datasource.local.preferences.utils.PreferencesDataStoreExtKt")
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
unmockkStatic("com.tangem.datasource.local.preferences.utils.PreferencesDataStoreExtKt")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN preference is true WHEN isCreationEnabled THEN emits true`() = runTest {
|
||||
every {
|
||||
appPreferencesStore.get(key = any<Preferences.Key<Boolean>>(), default = true)
|
||||
} returns flowOf(true)
|
||||
|
||||
val manager = DevHotWalletRestrictionManager(appPreferencesStore, dispatchers)
|
||||
|
||||
val emitted = getEmittedValues(manager.isCreationEnabled())
|
||||
|
||||
assertThat(emitted).containsExactly(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN preference is false WHEN isCreationEnabled THEN emits false`() = runTest {
|
||||
every {
|
||||
appPreferencesStore.get(key = any<Preferences.Key<Boolean>>(), default = true)
|
||||
} returns flowOf(false)
|
||||
|
||||
val manager = DevHotWalletRestrictionManager(appPreferencesStore, dispatchers)
|
||||
|
||||
val emitted = getEmittedValues(manager.isCreationEnabled())
|
||||
|
||||
assertThat(emitted).containsExactly(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN preference Flow emits true WHEN isCreationEnabledSync THEN returns cached true`() = runTest {
|
||||
every {
|
||||
appPreferencesStore.get(key = any<Preferences.Key<Boolean>>(), default = true)
|
||||
} returns flowOf(true)
|
||||
|
||||
val manager = DevHotWalletRestrictionManager(appPreferencesStore, dispatchers)
|
||||
|
||||
assertThat(manager.isCreationEnabledSync()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN preference Flow emits false WHEN isCreationEnabledSync THEN returns cached false`() = runTest {
|
||||
every {
|
||||
appPreferencesStore.get(key = any<Preferences.Key<Boolean>>(), default = true)
|
||||
} returns flowOf(false)
|
||||
|
||||
val manager = DevHotWalletRestrictionManager(appPreferencesStore, dispatchers)
|
||||
|
||||
assertThat(manager.isCreationEnabledSync()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN toggleCreationEnabled THEN opens editData transaction`() = runTest {
|
||||
every {
|
||||
appPreferencesStore.get(key = any<Preferences.Key<Boolean>>(), default = true)
|
||||
} returns flowOf(true)
|
||||
coEvery { appPreferencesStore.editData(any()) } returns mockk(relaxed = true)
|
||||
|
||||
val manager = DevHotWalletRestrictionManager(appPreferencesStore, dispatchers)
|
||||
manager.toggleCreationEnabled()
|
||||
|
||||
coVerify { appPreferencesStore.editData(any()) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.data.settings
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.test.core.getEmittedValues
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class ProdHotWalletRestrictionManagerTest {
|
||||
|
||||
private val manager = ProdHotWalletRestrictionManager()
|
||||
|
||||
@Test
|
||||
fun `WHEN isCreationEnabled THEN always emits true`() = runTest {
|
||||
val emitted = getEmittedValues(manager.isCreationEnabled())
|
||||
|
||||
assertThat(emitted).containsExactly(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN isCreationEnabledSync THEN returns true`() = runTest {
|
||||
assertThat(manager.isCreationEnabledSync()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN toggleCreationEnabled THEN isCreationEnabledSync still returns true`() = runTest {
|
||||
manager.toggleCreationEnabled()
|
||||
assertThat(manager.isCreationEnabledSync()).isTrue()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue