diff --git a/.gitignore b/.gitignore
index 6106311a37..0144ddc6e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,10 @@ local.properties
# Google services
+
+# Huawei services
+app/agconnect-services.json
+
app/src/debug/google-services.json
app/src/internal/google-services.json
app/src/external/google-services.json
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 17ee83e2e2..720052668f 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -11,9 +11,14 @@ plugins {
alias(deps.plugins.firebase.crashlytics)
alias(deps.plugins.firebase.perf)
alias(deps.plugins.ksp)
+ id(deps.plugins.agconnect.get().pluginId)
id("configuration")
}
+agcp {
+ manifest = false
+}
+
android {
namespace = "com.tangem.wallet"
testOptions {
@@ -53,12 +58,35 @@ android {
keyPassword = keystoreProperties["key_password"] as String
}
}
+
+ flavorDimensions += "services"
+
+ productFlavors {
+ create("google") {
+ dimension = "services"
+ buildConfigField("String", "FLAVOR_NAME", "\"google\"")
+ }
+ create("huawei") {
+ dimension = "services"
+ buildConfigField("String", "FLAVOR_NAME", "\"huawei\"")
+ }
+ }
+
+ buildTypes {
+ debug {
+ buildConfigField("String", "BUILD_TYPE", "\"debug\"")
+ }
+ release {
+ buildConfigField("String", "BUILD_TYPE", "\"release\"")
+ }
+ }
}
configurations.all {
exclude(group = "org.bouncycastle", module = "bcprov-jdk15to18")
exclude(group = "com.github.komputing.kethereum")
+ exclude(group = "com.android.tools.build", module = "gradle")
resolutionStrategy {
dependencySubstitution {
@@ -379,4 +407,9 @@ dependencies {
// excludes version 9999.0-empty-to-avoid-conflict-with-guava
exclude(group = "com.google.guava", module = "listenablefuture")
}
+
+ /** Huawei flavor-specific dependencies */
+ "huaweiImplementation"(deps.huawei.push)
+ "huaweiImplementation"(deps.agconnect.agcp)
+ "huaweiImplementation"(deps.agconnect.core)
}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 2a7b205c9d..4e36894e88 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -7,6 +7,13 @@
-keep class com.google.android.gms.internal.** { *; }
-keepclasseswithmembers class com.google.firebase.FirebaseException
+# huawei push kit
+-ignorewarnings
+-keepattributes SourceFile,LineNumberTable
+-keep class com.huawei.hianalytics.**{*;}
+-keep class com.huawei.updatesdk.**{*;}
+-keep class com.huawei.hms.**{*;}
+
# hedera sdk
-keep class com.hedera.hashgraph.sdk.** { *; }
-keep interface com.hedera.hashgraph.sdk.** { *; }
diff --git a/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt b/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt
index 823d7df27c..7fd98efe81 100644
--- a/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt
+++ b/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt
@@ -14,6 +14,8 @@ import com.kaspersky.kaspresso.kaspresso.Kaspresso
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
import com.tangem.common.allure.FailedStepScreenshotInterceptor
import com.tangem.common.rules.ApiEnvironmentRule
+import com.tangem.core.configtoggle.feature.FeatureTogglesManager
+import com.tangem.core.configtoggle.feature.MutableFeatureTogglesManager
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
@@ -46,6 +48,9 @@ abstract class BaseTestCase : TestCase(
@Inject
lateinit var appPreferencesStore: AppPreferencesStore
+ @Inject
+ lateinit var featureTogglesManager: FeatureTogglesManager
+
private val hiltRule = HiltAndroidRule(this)
private val apiEnvironmentRule = ApiEnvironmentRule()
private val permissionRule = GrantPermissionRule.grant(
@@ -90,6 +95,7 @@ abstract class BaseTestCase : TestCase(
apiEnvironmentRule.setup(apiConfigsManager)
ActivityScenario.launch(MainActivity::class.java)
Intents.init()
+ setFeatureToggles()
additionalBeforeSection()
}.after {
additionalAfterSection()
@@ -113,4 +119,14 @@ abstract class BaseTestCase : TestCase(
{
composeTestRule.onRoot(useUnmergedTree = useUnmergedTree).printToLog(tag, maxDepth)
}
+
+
+ private fun setFeatureToggles() {
+ runBlocking {
+ with(featureTogglesManager as MutableFeatureTogglesManager) {
+ changeToggle("WALLET_CONNECT_REDESIGN_ENABLED", true)
+ changeToggle("WALLET_BALANCE_FETCHER_ENABLED", true)
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/data/FirebasePushNotificationsTokenProvider.kt b/app/src/google/java/com/tangem/tap/FirebasePushNotificationsTokenProvider.kt
similarity index 95%
rename from app/src/main/java/com/tangem/tap/data/FirebasePushNotificationsTokenProvider.kt
rename to app/src/google/java/com/tangem/tap/FirebasePushNotificationsTokenProvider.kt
index 358bdd3d61..627e41f2cc 100644
--- a/app/src/main/java/com/tangem/tap/data/FirebasePushNotificationsTokenProvider.kt
+++ b/app/src/google/java/com/tangem/tap/FirebasePushNotificationsTokenProvider.kt
@@ -1,4 +1,4 @@
-package com.tangem.tap.data
+package com.tangem.tap
import com.google.firebase.messaging.FirebaseMessaging
import com.tangem.utils.notifications.PushNotificationsTokenProvider
diff --git a/app/src/main/java/com/tangem/tap/di/data/PushNotificationsModule.kt b/app/src/google/java/com/tangem/tap/di/GooglePushModule.kt
similarity index 74%
rename from app/src/main/java/com/tangem/tap/di/data/PushNotificationsModule.kt
rename to app/src/google/java/com/tangem/tap/di/GooglePushModule.kt
index 063efaf3dc..a8e4e87c08 100644
--- a/app/src/main/java/com/tangem/tap/di/data/PushNotificationsModule.kt
+++ b/app/src/google/java/com/tangem/tap/di/GooglePushModule.kt
@@ -1,6 +1,6 @@
-package com.tangem.tap.di.data
+package com.tangem.tap.di
-import com.tangem.tap.data.FirebasePushNotificationsTokenProvider
+import com.tangem.tap.FirebasePushNotificationsTokenProvider
import com.tangem.utils.notifications.PushNotificationsTokenProvider
import dagger.Binds
import dagger.Module
@@ -10,7 +10,7 @@ import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
-internal interface PushNotificationsModule {
+internal interface GooglePushModule {
@Binds
@Singleton
diff --git a/app/src/huawei/AndroidManifest.xml b/app/src/huawei/AndroidManifest.xml
new file mode 100644
index 0000000000..0c769dbe3e
--- /dev/null
+++ b/app/src/huawei/AndroidManifest.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/huawei/java/com/tangem/tap/HuaweiPushNotificationsTokenProvider.kt b/app/src/huawei/java/com/tangem/tap/HuaweiPushNotificationsTokenProvider.kt
new file mode 100644
index 0000000000..03c709cd6c
--- /dev/null
+++ b/app/src/huawei/java/com/tangem/tap/HuaweiPushNotificationsTokenProvider.kt
@@ -0,0 +1,50 @@
+package com.tangem.tap
+
+import android.content.Context
+import com.google.firebase.messaging.FirebaseMessaging
+import com.huawei.agconnect.AGConnectOptionsBuilder
+import com.huawei.hms.aaid.HmsInstanceId
+import com.huawei.hms.common.ApiException
+import com.tangem.google.GoogleServicesHelper
+import com.tangem.utils.coroutines.CoroutineDispatcherProvider
+import com.tangem.utils.notifications.PushNotificationsTokenProvider
+import dagger.hilt.android.qualifiers.ApplicationContext
+import kotlinx.coroutines.tasks.await
+import kotlinx.coroutines.withContext
+import timber.log.Timber
+import javax.inject.Inject
+
+internal class HuaweiPushNotificationsTokenProvider @Inject constructor(
+ @ApplicationContext private val context: Context,
+ private val coroutineDispatcherProvider: CoroutineDispatcherProvider,
+) : PushNotificationsTokenProvider {
+
+ override suspend fun getToken(): String {
+ val isGoogleServicesAvailable = GoogleServicesHelper.checkGoogleServicesAvailability(context)
+ return if (isGoogleServicesAvailable) {
+ try {
+ FirebaseMessaging.getInstance().token.await()
+ } catch (ex: Exception) {
+ Timber.e(ex)
+ ""
+ }
+ } else {
+ withContext(coroutineDispatcherProvider.io) {
+ try {
+ val appId = AGConnectOptionsBuilder().build(context).getString(APP_ID_KEY)
+ val token = HmsInstanceId.getInstance(context).getToken(appId, TOKEN_REQUEST_MODE)
+ Timber.i("Requested token from HuaweiService: $token")
+ token
+ } catch (e: ApiException) {
+ Timber.i("Fetching token from HuaweiService failed cause: ${e.message}")
+ ""
+ }
+ }
+ }
+ }
+
+ companion object {
+ private const val APP_ID_KEY = "client/app_id"
+ private const val TOKEN_REQUEST_MODE = "HCM"
+ }
+}
\ No newline at end of file
diff --git a/app/src/huawei/java/com/tangem/tap/HuaweiPushService.kt b/app/src/huawei/java/com/tangem/tap/HuaweiPushService.kt
new file mode 100644
index 0000000000..b7a1de528d
--- /dev/null
+++ b/app/src/huawei/java/com/tangem/tap/HuaweiPushService.kt
@@ -0,0 +1,47 @@
+package com.tangem.tap
+
+import android.os.Bundle
+import com.huawei.hms.push.HmsMessageService
+import com.huawei.hms.push.RemoteMessage
+import com.tangem.google.GoogleServicesHelper
+import com.tangem.tap.common.pushes.PushNotificationDelegate
+import timber.log.Timber
+
+class HuaweiPushService : HmsMessageService() {
+
+ private val pushNotificationDelegate: PushNotificationDelegate by lazy {
+ PushNotificationDelegate(applicationContext)
+ }
+
+ override fun onNewToken(token: String?, bundle: Bundle?) {
+ super.onNewToken(token, bundle)
+ Timber.i("HuaweiPushService: On new token from HuaweiService: $token")
+ }
+
+ override fun onTokenError(e: Exception?, bundle: Bundle?) {
+ super.onTokenError(e, bundle)
+ Timber.i("HuaweiPushService: Fetching token from HuaweiService failed cause: ${e?.message}")
+ }
+
+ override fun onMessageReceived(message: RemoteMessage?) {
+ super.onMessageReceived(message)
+ val isGoogleServicesAvailable = GoogleServicesHelper.checkGoogleServicesAvailability(this)
+ if (isGoogleServicesAvailable) return
+ val notification = message?.notification ?: return
+ val channelId = notification.channelId ?: TANGEM_CHANNEL_ID
+
+ pushNotificationDelegate.showNotification(
+ dataMap = message.dataOfMap,
+ title = notification.title,
+ body = notification.body,
+ channelId = channelId,
+ priority = message.urgency,
+ imageUrl = notification.imageUrl,
+ vibratePattern = notification.vibrateConfig,
+ )
+ }
+
+ private companion object {
+ const val TANGEM_CHANNEL_ID = "Tangem General" // General channel for notifications
+ }
+}
\ No newline at end of file
diff --git a/app/src/huawei/java/com/tangem/tap/di/HuaweiPushModule.kt b/app/src/huawei/java/com/tangem/tap/di/HuaweiPushModule.kt
new file mode 100644
index 0000000000..f4379c5469
--- /dev/null
+++ b/app/src/huawei/java/com/tangem/tap/di/HuaweiPushModule.kt
@@ -0,0 +1,18 @@
+package com.tangem.tap.di
+
+import com.tangem.tap.HuaweiPushNotificationsTokenProvider
+import com.tangem.utils.notifications.PushNotificationsTokenProvider
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+internal interface HuaweiPushModule {
+
+ @Binds
+ @Singleton
+ fun bindPushNotificationsTokenProvider(impl: HuaweiPushNotificationsTokenProvider): PushNotificationsTokenProvider
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt
index 09c556cae1..a413d65b34 100644
--- a/app/src/main/java/com/tangem/tap/TangemApplication.kt
+++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt
@@ -77,6 +77,7 @@ import com.tangem.wallet.BuildConfig
import dagger.hilt.EntryPoints
import kotlinx.coroutines.*
import org.rekotlin.Store
+import timber.log.Timber
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository as WalletConnect2Repository
lateinit var store: Store
@@ -289,13 +290,17 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
tangemAppLoggerInitializer.initialize()
+ Timber.i("APP STARTED")
+ if (BuildConfig.TESTER_MENU_ENABLED) {
+ Timber.i(featureTogglesManager.toString())
+ }
+
foregroundActivityObserver = ForegroundActivityObserver()
registerActivityLifecycleCallbacks(foregroundActivityObserver.callbacks)
// We need to initialize the toggles and excludedBlockchainsManager before the MainActivity starts using them.
runBlocking {
awaitAll(
- async { featureTogglesManager.init() },
async { excludedBlockchainsManager.init() },
)
initWithConfigDependency(environmentConfig = environmentConfigStorage.initialize())
diff --git a/app/src/main/java/com/tangem/tap/common/buildconfig/AppConfigurationProviderImpl.kt b/app/src/main/java/com/tangem/tap/common/buildconfig/AppConfigurationProviderImpl.kt
new file mode 100644
index 0000000000..7a87d69e7c
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/buildconfig/AppConfigurationProviderImpl.kt
@@ -0,0 +1,13 @@
+package com.tangem.tap.common.buildconfig
+
+import com.tangem.utils.buildConfig.AppConfigurationProvider
+import com.tangem.wallet.BuildConfig
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+internal class AppConfigurationProviderImpl @Inject constructor() : AppConfigurationProvider {
+
+ override fun isDebug(): Boolean = BuildConfig.BUILD_TYPE == "debug"
+ override fun isHuawei(): Boolean = BuildConfig.FLAVOR_NAME == "huawei"
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/pushes/PushNotificationDelegate.kt b/app/src/main/java/com/tangem/tap/common/pushes/PushNotificationDelegate.kt
new file mode 100644
index 0000000000..844eec86ff
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/pushes/PushNotificationDelegate.kt
@@ -0,0 +1,102 @@
+package com.tangem.tap.common.pushes
+
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.graphics.Bitmap
+import android.net.Uri
+import android.os.Build
+import androidx.core.app.NotificationCompat
+import androidx.core.content.ContextCompat
+import androidx.core.graphics.drawable.toBitmap
+import coil.executeBlocking
+import coil.request.ImageRequest
+import com.tangem.domain.common.LogConfig
+import com.tangem.tap.MainActivity
+import com.tangem.tap.common.images.createCoilImageLoader
+import com.tangem.tap.features.intentHandler.handlers.OnPushClickedIntentHandler
+import com.tangem.wallet.R
+
+class PushNotificationDelegate(private val context: Context) {
+
+ @Suppress("LongParameterList")
+ fun showNotification(
+ dataMap: Map,
+ title: String?,
+ body: String?,
+ channelId: String,
+ priority: Int,
+ imageUrl: Uri? = null,
+ vibratePattern: LongArray?,
+ ) {
+ val intent = Intent(context, MainActivity::class.java).apply {
+ dataMap.forEach { (key, value) ->
+ putExtra(key, value)
+ }
+ putExtra(OnPushClickedIntentHandler.OPENED_FROM_GCM_PUSH, true)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ }
+
+ val pendingIntent = PendingIntent.getActivity(
+ /* context = */ context,
+ /* requestCode = */ PUSH_NOTIFICATION_REQUEST_CODE,
+ /* intent = */ intent,
+ /* flags = */ PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE,
+ )
+
+ val notificationBuilder = NotificationCompat.Builder(context, channelId)
+ .setSmallIcon(R.drawable.ic_tangem_24)
+ .setContentTitle(title)
+ .setContentText(body)
+ .setPriority(priority)
+ .setAutoCancel(true)
+ .setContentIntent(pendingIntent)
+ .setVibrate(vibratePattern)
+ .apply {
+ imageUrl?.let { uri ->
+ val bitmap = getBitmapImageFromUrl(uri)
+ setStyle(
+ NotificationCompat
+ .BigPictureStyle()
+ .bigPicture(bitmap),
+ ).setLargeIcon(bitmap)
+ }
+ }
+
+ val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val notificationChannel = NotificationChannel(
+ channelId,
+ ContextCompat.getString(context, R.string.tangem_app_name),
+ NotificationManager.IMPORTANCE_HIGH,
+ )
+ notificationManager.createNotificationChannel(notificationChannel)
+ }
+
+ // Generating unique notification id
+ val uniqueId = (System.currentTimeMillis() % Integer.MAX_VALUE).toInt()
+
+ notificationManager.notify(
+ /* id = */ uniqueId,
+ /* notification = */ notificationBuilder.build(),
+ )
+ }
+
+ private fun getBitmapImageFromUrl(url: Uri): Bitmap? {
+ return createCoilImageLoader(
+ context,
+ logEnabled = LogConfig.imageLoader,
+ ).executeBlocking(
+ ImageRequest.Builder(context)
+ .data(url)
+ .build(),
+ ).drawable?.toBitmap()
+ }
+
+ private companion object {
+ const val PUSH_NOTIFICATION_REQUEST_CODE = 123
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/pushes/TangemPushNotificationService.kt b/app/src/main/java/com/tangem/tap/common/pushes/TangemPushNotificationService.kt
index ebeec6dffc..97026725ea 100644
--- a/app/src/main/java/com/tangem/tap/common/pushes/TangemPushNotificationService.kt
+++ b/app/src/main/java/com/tangem/tap/common/pushes/TangemPushNotificationService.kt
@@ -1,30 +1,17 @@
package com.tangem.tap.common.pushes
import android.annotation.SuppressLint
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.content.Intent
-import android.graphics.Bitmap
-import android.net.Uri
-import android.os.Build
-import androidx.core.app.NotificationCompat
-import androidx.core.content.ContextCompat
-import androidx.core.graphics.drawable.toBitmap
-import coil.executeBlocking
-import coil.request.ImageRequest
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
-import com.tangem.domain.common.LogConfig
-import com.tangem.tap.MainActivity
-import com.tangem.tap.common.images.createCoilImageLoader
-import com.tangem.tap.features.intentHandler.handlers.OnPushClickedIntentHandler
-import com.tangem.wallet.R
import timber.log.Timber
@SuppressLint("MissingFirebaseInstanceTokenRefresh")
internal class TangemPushNotificationService : FirebaseMessagingService() {
+ private val pushNotificationDelegate: PushNotificationDelegate by lazy {
+ PushNotificationDelegate(applicationContext)
+ }
+
override fun onNewToken(token: String) {
super.onNewToken(token)
Timber.d("New FCM token received: $token")
@@ -36,73 +23,18 @@ internal class TangemPushNotificationService : FirebaseMessagingService() {
val notification = message.notification ?: return
val channelId = notification.channelId ?: TANGEM_CHANNEL_ID
- val intent = Intent(applicationContext, MainActivity::class.java).apply {
- message.data.forEach {
- putExtra(it.key, it.value)
- }
- putExtra(OnPushClickedIntentHandler.OPENED_FROM_GCM_PUSH, true)
- addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
- }
- val pendingIntent = PendingIntent.getActivity(
- /* context = */ this,
- /* requestCode = */ PUSH_NOTIFICATION_REQUEST_CODE,
- /* intent = */ intent,
- /* flags = */ PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE,
+ pushNotificationDelegate.showNotification(
+ dataMap = message.data,
+ title = notification.title,
+ body = notification.body,
+ channelId = channelId,
+ priority = message.priority,
+ imageUrl = notification.imageUrl,
+ vibratePattern = notification.vibrateTimings,
)
-
- val notificationBuilder =
- NotificationCompat.Builder(applicationContext, channelId)
- .setSmallIcon(R.drawable.ic_tangem_24)
- .setContentTitle(notification.title)
- .setContentText(notification.body)
- .setPriority(message.priority)
- .setAutoCancel(true)
- .setContentIntent(pendingIntent)
- .setVibrate(notification.vibrateTimings)
- .apply {
- notification.imageUrl?.let { uri ->
- val bitmap = getBitmapImageFromUrl(uri)
- setStyle(
- NotificationCompat
- .BigPictureStyle()
- .bigPicture(bitmap),
- ).setLargeIcon(bitmap)
- }
- }
-
- val notificationManager = applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- val notificationChannel = NotificationChannel(
- channelId,
- ContextCompat.getString(applicationContext, R.string.tangem_app_name),
- NotificationManager.IMPORTANCE_HIGH,
- )
- notificationManager.createNotificationChannel(notificationChannel)
- }
-
- // Generating unique notification id
- val uniqueId = (System.currentTimeMillis() % Integer.MAX_VALUE).toInt()
-
- notificationManager.notify(
- /* id = */ uniqueId,
- /* notification = */ notificationBuilder.build(),
- )
- }
-
- private fun getBitmapImageFromUrl(url: Uri): Bitmap? {
- return createCoilImageLoader(
- applicationContext,
- logEnabled = LogConfig.imageLoader,
- ).executeBlocking(
- ImageRequest.Builder(applicationContext)
- .data(url)
- .build(),
- ).drawable?.toBitmap()
}
private companion object {
const val TANGEM_CHANNEL_ID = "Tangem General" // General channel for notifications
- const val PUSH_NOTIFICATION_REQUEST_CODE = 123
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/di/AppConfigurationModule.kt b/app/src/main/java/com/tangem/tap/di/AppConfigurationModule.kt
new file mode 100644
index 0000000000..42148c10d5
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/di/AppConfigurationModule.kt
@@ -0,0 +1,18 @@
+package com.tangem.tap.di
+
+import com.tangem.tap.common.buildconfig.AppConfigurationProviderImpl
+import com.tangem.utils.buildConfig.AppConfigurationProvider
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+internal interface AppConfigurationModule {
+
+ @Binds
+ @Singleton
+ fun bindAppConfigurationProvider(impl: AppConfigurationProviderImpl): AppConfigurationProvider
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt
index 44dd702fc3..911eededb2 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt
@@ -25,7 +25,7 @@ import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
-@Suppress("TooManyFunctions")
+@Suppress("TooManyFunctions", "LargeClass")
@Module
@InstallIn(SingletonComponent::class)
internal object WalletsDomainModule {
@@ -352,4 +352,24 @@ internal object WalletsDomainModule {
walletsRepository = walletsRepository,
)
}
+
+ @Provides
+ @Singleton
+ fun providesIsUpgradeWalletNotificationEnabledUseCase(
+ walletsRepository: WalletsRepository,
+ ): IsUpgradeWalletNotificationEnabledUseCase {
+ return IsUpgradeWalletNotificationEnabledUseCase(
+ walletsRepository = walletsRepository,
+ )
+ }
+
+ @Provides
+ @Singleton
+ fun providesDismissUpgradeWalletNotificationUseCase(
+ walletsRepository: WalletsRepository,
+ ): DismissUpgradeWalletNotificationUseCase {
+ return DismissUpgradeWalletNotificationUseCase(
+ walletsRepository = walletsRepository,
+ )
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/di/hot/TangemHotSdkModule.kt b/app/src/main/java/com/tangem/tap/di/hot/TangemHotSdkModule.kt
index c21b55928b..53110dc9c9 100644
--- a/app/src/main/java/com/tangem/tap/di/hot/TangemHotSdkModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/hot/TangemHotSdkModule.kt
@@ -1,5 +1,7 @@
package com.tangem.tap.di.hot
+import com.tangem.data.wallets.hot.DefaultHotWalletAccessor
+import com.tangem.domain.wallets.hot.HotWalletAccessor
import com.tangem.hot.sdk.TangemHotSdk
import com.tangem.tap.features.hot.TangemHotSDKProxy
import dagger.Binds
@@ -15,4 +17,8 @@ internal interface TangemHotSdkModule {
@Binds
@Singleton
fun bindTangemHotSdk(proxy: TangemHotSDKProxy): TangemHotSdk
+
+ @Binds
+ @Singleton
+ fun bindHotWalletAccessor(default: DefaultHotWalletAccessor): HotWalletAccessor
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
index 615b1c3778..9a80508e7b 100644
--- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
+++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
@@ -17,6 +17,7 @@ import com.tangem.features.disclaimer.api.components.DisclaimerComponent
import com.tangem.features.home.api.HomeComponent
import com.tangem.features.hotwallet.AddExistingWalletComponent
import com.tangem.features.hotwallet.CreateMobileWalletComponent
+import com.tangem.features.hotwallet.UpgradeWalletComponent
import com.tangem.features.hotwallet.WalletActivationComponent
import com.tangem.features.hotwallet.CreateWalletBackupComponent
import com.tangem.features.hotwallet.UpdateAccessCodeComponent
@@ -103,6 +104,7 @@ internal class ChildFactory @Inject constructor(
private val chooseManagedTokensComponentFactory: ChooseManagedTokensComponent.Factory,
private val createWalletSelectionComponentFactory: CreateWalletSelectionComponent.Factory,
private val createMobileWalletComponentFactory: CreateMobileWalletComponent.Factory,
+ private val upgradeWalletComponentFactory: UpgradeWalletComponent.Factory,
private val addExistingWalletComponentFactory: AddExistingWalletComponent.Factory,
private val walletActivationComponentFactory: WalletActivationComponent.Factory,
private val createWalletBackupComponentFactory: CreateWalletBackupComponent.Factory,
@@ -486,6 +488,15 @@ internal class ChildFactory @Inject constructor(
componentFactory = createMobileWalletComponentFactory,
)
}
+ is AppRoute.UpgradeWallet -> {
+ createComponentChild(
+ context = context,
+ params = UpgradeWalletComponent.Params(
+ userWalletId = route.userWalletId,
+ ),
+ componentFactory = upgradeWalletComponentFactory,
+ )
+ }
is AppRoute.AddExistingWallet -> {
createComponentChild(
context = context,
diff --git a/app/src/main/res/drawable/inset_splash.xml b/app/src/main/res/drawable/inset_splash.xml
index 1d66dc1301..2886ecbd00 100644
--- a/app/src/main/res/drawable/inset_splash.xml
+++ b/app/src/main/res/drawable/inset_splash.xml
@@ -1,10 +1,10 @@
+ android:insetLeft="55dp"
+ android:insetRight="55dp"
+ android:insetTop="55dp"
+ android:insetBottom="55dp"/>
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index db52974e28..bf4703e8e2 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -26,6 +26,4 @@
#1E1E1E
#656565
- #000000
-
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 2b866d0894..d91dbb1dbe 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -16,7 +16,7 @@
diff --git a/build.gradle.kts b/build.gradle.kts
index c490aef8c2..5d73b767fa 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -16,6 +16,13 @@ plugins {
alias(deps.plugins.ksp) apply false
}
+buildscript {
+ dependencies {
+ classpath(deps.gradle.android)
+ classpath(deps.agconnect.agcp)
+ }
+}
+
val clean by tasks.registering {
delete(rootProject.buildDir)
}
diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
index d0ffcca403..cfa1070c25 100644
--- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
+++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
@@ -311,6 +311,11 @@ sealed class AppRoute(val path: String) : Route {
@Serializable
object CreateMobileWallet : AppRoute(path = "/create_mobile_wallet")
+ @Serializable
+ data class UpgradeWallet(
+ val userWalletId: UserWalletId,
+ ) : AppRoute(path = "/upgrade_wallet/${userWalletId.stringValue}")
+
@Serializable
object AddExistingWallet : AppRoute(path = "/add_existing_wallet")
diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountRow.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountRow.kt
index c48fa40072..d02fe62c15 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountRow.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountRow.kt
@@ -9,6 +9,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.common.ui.R
import com.tangem.core.ui.extensions.TextReference
@@ -73,6 +74,8 @@ private fun Title(title: TextReference) {
text = title.resolveReference(),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.primary1,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
)
}
@@ -82,6 +85,8 @@ private fun Subtitle(subtitle: TextReference) {
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.caption2,
text = subtitle.resolveReference(),
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
)
}
diff --git a/common/ui/src/main/java/com/tangem/common/ui/userwallet/UserWalletItem.kt b/common/ui/src/main/java/com/tangem/common/ui/userwallet/UserWalletItem.kt
index 31c66358b4..ad44dcf7dd 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/userwallet/UserWalletItem.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/userwallet/UserWalletItem.kt
@@ -139,33 +139,59 @@ private fun NameAndInfo(
)
}
}
- Text(
- text = " $DOT ",
- style = TangemTheme.typography.caption2,
- color = TangemTheme.colors.text.tertiary,
- maxLines = 1,
- )
- AnimatedContent(
- targetState = balance,
- label = "Balance content",
- ) { balance ->
- val (balanceValue, isFlickering) = getBalanceValueAndFlickerState(balance)
+ BalanceContent(balance)
+ }
+ }
+}
- if (balanceValue == null) {
- TextShimmer(
- style = TangemTheme.typography.caption2,
- text = "aaaaa",
- )
- } else {
+@Composable
+private fun BalanceContent(balance: UserWalletItemUM.Balance, modifier: Modifier = Modifier) {
+ AnimatedContent(
+ modifier = modifier,
+ targetState = balance,
+ label = "Balance content",
+ ) { balance ->
+ when (balance) {
+ UserWalletItemUM.Balance.Locked -> {
+ Icon(
+ modifier = Modifier
+ .padding(start = 4.dp, bottom = 2.dp, top = 2.dp)
+ .size(12.dp),
+ imageVector = ImageVector.vectorResource(R.drawable.ic_lock_24),
+ tint = TangemTheme.colors.icon.informative,
+ contentDescription = null,
+ )
+ }
+ UserWalletItemUM.Balance.NotShowing -> {
+ /** No balance and no dot */
+ }
+ else -> {
+ Row {
Text(
- text = balanceValue,
- style = TangemTheme.typography.caption2.applyBladeBrush(
- isEnabled = isFlickering,
- textColor = TangemTheme.colors.text.tertiary,
- ),
+ text = " $DOT ",
+ style = TangemTheme.typography.caption2,
+ color = TangemTheme.colors.text.tertiary,
maxLines = 1,
)
+
+ val (balanceValue, isFlickering) = getBalanceValueAndFlickerState(balance)
+
+ if (balanceValue == null) {
+ TextShimmer(
+ style = TangemTheme.typography.caption2,
+ text = "aaaaa",
+ )
+ } else {
+ Text(
+ text = balanceValue,
+ style = TangemTheme.typography.caption2.applyBladeBrush(
+ isEnabled = isFlickering,
+ textColor = TangemTheme.colors.text.tertiary,
+ ),
+ maxLines = 1,
+ )
+ }
}
}
}
@@ -246,9 +272,8 @@ fun getBalanceValueAndFlickerState(balance: UserWalletItemUM.Balance): Pair DASH_SIGN to false
is UserWalletItemUM.Balance.Hidden -> THREE_STARS to false
- is UserWalletItemUM.Balance.Loading -> null to false
- is UserWalletItemUM.Balance.Locked -> stringResourceSafe(R.string.common_locked) to false
is UserWalletItemUM.Balance.Loaded -> balance.value to balance.isFlickering
+ else -> null to false
}
}
@@ -392,6 +417,15 @@ private class UserWalletItemUMPreviewProvider : PreviewParameterProvider {
- private val artworkUMConverter = ArtworkUMConverter()
+ private val artwork = artwork ?: UserWalletItemUM.ImageState.Loading
override fun convert(value: UserWallet): UserWalletItemUM {
return with(value) {
@@ -52,7 +51,7 @@ class UserWalletItemUMConverter(
isEnabled = isEnabled(userWallet = this),
endIcon = endIcon,
onClick = { onClick(value.walletId) },
- imageState = getImageState(userWallet = value),
+ imageState = artwork,
label = getLabelOrNull(userWallet = this),
)
}
@@ -73,14 +72,6 @@ class UserWalletItemUMConverter(
}
}
- private fun getImageState(userWallet: UserWallet): UserWalletItemUM.ImageState {
- return when {
- userWallet is UserWallet.Hot -> UserWalletItemUM.ImageState.MobileWallet
- artwork != null -> UserWalletItemUM.ImageState.Image(artworkUMConverter.convert(artwork))
- else -> UserWalletItemUM.ImageState.Loading
- }
- }
-
private fun getInfo(userWallet: UserWallet): UserWalletItemUM.Information.Loaded {
val text = when (userWallet) {
is UserWallet.Cold -> {
@@ -100,8 +91,9 @@ class UserWalletItemUMConverter(
private fun getBalanceInfo(userWallet: UserWallet): UserWalletItemUM.Balance {
return when {
- isBalanceHidden -> UserWalletItemUM.Balance.Hidden
userWallet.isLocked -> UserWalletItemUM.Balance.Locked
+ authMode -> UserWalletItemUM.Balance.NotShowing
+ isBalanceHidden -> UserWalletItemUM.Balance.Hidden
balance == null -> UserWalletItemUM.Balance.Loading
else -> {
when (balance) {
diff --git a/common/ui/src/main/java/com/tangem/common/ui/userwallet/state/UserWalletItemUM.kt b/common/ui/src/main/java/com/tangem/common/ui/userwallet/state/UserWalletItemUM.kt
index c0abb2b485..b31b4d5ddb 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/userwallet/state/UserWalletItemUM.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/userwallet/state/UserWalletItemUM.kt
@@ -28,6 +28,8 @@ data class UserWalletItemUM(
data object Hidden : Balance()
+ data object NotShowing : Balance()
+
data object Locked : Balance()
data object Failed : Balance()
diff --git a/core/config-toggles/build.gradle.kts b/core/config-toggles/build.gradle.kts
index 96fb87b639..368af3bc77 100644
--- a/core/config-toggles/build.gradle.kts
+++ b/core/config-toggles/build.gradle.kts
@@ -1,4 +1,6 @@
-import com.tangem.plugin.configuration.configurations.extension.kaptForObfuscatingVariants
+import com.squareup.kotlinpoet.*
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import org.json.JSONArray
plugins {
alias(deps.plugins.android.library)
@@ -9,8 +11,24 @@ plugins {
id("configuration")
}
+buildscript {
+ dependencies {
+ classpath("com.squareup:kotlinpoet:1.15.0")
+ classpath("org.json:json:20231013")
+ }
+}
+
android {
namespace = "com.tangem.core.configtoggle"
+ sourceSets["main"].java.srcDir("build/generated/source/toggles")
+}
+
+tasks.named("preBuild") {
+ dependsOn(generateFeatureToggles /*generateExcludedBlockchainToggles*/)
+}
+
+tasks.withType().configureEach {
+ useJUnitPlatform()
}
dependencies {
@@ -32,7 +50,72 @@ dependencies {
implementation(projects.core.utils)
testImplementation(deps.test.coroutine)
- testImplementation(deps.test.junit)
+ testImplementation(deps.test.junit5)
+ testRuntimeOnly(deps.test.junit5.engine)
testImplementation(deps.test.mockk)
testImplementation(deps.test.truth)
+ testImplementation(projects.common.test)
+}
+
+val generateFeatureToggles by tasks.registering {
+ generateToggles(
+ inputFilePath = "src/main/assets/configs/feature_toggles_config.json",
+ generatedFileName = "FeatureToggles",
+ )
+}
+
+val generateExcludedBlockchainToggles by tasks.registering {
+ generateToggles(
+ inputFilePath = "src/main/assets/configs/excluded_blockchains_config.json",
+ generatedFileName = "ExcludedBlockchainToggles",
+ )
+}
+
+fun Task.generateToggles(inputFilePath: String, generatedFileName: String) {
+ val inputFile = file(inputFilePath)
+ val outputDir = file("build/generated/source/toggles")
+
+ inputs.file(inputFile)
+ outputs.dir(outputDir)
+
+ doLast {
+ val jsonText = inputFile.readText()
+ val jsonArray = JSONArray(jsonText)
+
+ val entries = (0 until jsonArray.length()).map { i ->
+ val obj = jsonArray.getJSONObject(i)
+ val name = obj.getString("name")
+ val version = obj.getString("version")
+ CodeBlock.of("%S to %S", name, version)
+ }
+
+ val mapInitializer = CodeBlock.builder()
+ .add("mapOf(\n")
+ .indent()
+ .apply {
+ entries.forEachIndexed { index, entry ->
+ add(entry)
+ if (index != entries.lastIndex) add(",\n") else add("\n")
+ }
+ }
+ .unindent()
+ .add(")")
+ .build()
+
+ val objectBuilder = TypeSpec.objectBuilder(name = generatedFileName)
+ .addKdoc("Generated from $inputFilePath")
+ .addProperty(
+ PropertySpec.builder("values", MAP.parameterizedBy(STRING, STRING))
+ .initializer(mapInitializer)
+ .build()
+ )
+
+ val fileSpec = FileSpec.builder(packageName = "com.tangem.core.configtoggle", fileName = generatedFileName)
+ .addType(objectBuilder.build())
+ .build()
+
+ val outputPackageDir = File(outputDir, "")
+ outputPackageDir.mkdirs()
+ fileSpec.writeTo(outputPackageDir)
+ }
}
\ No newline at end of file
diff --git a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
index bd1f7e6e67..9d99dceed5 100644
--- a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
+++ b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
@@ -62,5 +62,13 @@
{
"name": "NEW_TOKEN_RECEIVE_ENABLED",
"version": "5.28.0"
+ },
+ {
+ "name": "YIELD_LENDING_FEATURE_ENABLED",
+ "version": "undefined"
+ },
+ {
+ "name": "NEW_ONRAMP_MAIN_ENABLED",
+ "version": "undefined"
}
]
diff --git a/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/di/FeatureTogglesManagerModule.kt b/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/di/FeatureTogglesManagerModule.kt
index fa5c04f6a1..79ad0221fc 100644
--- a/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/di/FeatureTogglesManagerModule.kt
+++ b/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/di/FeatureTogglesManagerModule.kt
@@ -5,16 +5,14 @@ import com.tangem.core.configtoggle.BuildConfig
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.core.configtoggle.feature.impl.DevFeatureTogglesManager
import com.tangem.core.configtoggle.feature.impl.ProdFeatureTogglesManager
-import com.tangem.core.configtoggle.storage.LocalTogglesStorage
+import com.tangem.core.configtoggle.storage.FeatureTogglesLocalStorage
import com.tangem.core.configtoggle.version.DefaultVersionProvider
-import com.tangem.datasource.asset.loader.AssetLoader
import com.tangem.datasource.local.preferences.AppPreferencesStore
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
-import kotlinx.coroutines.runBlocking
import javax.inject.Singleton
@Module
@@ -25,29 +23,17 @@ internal object FeatureTogglesManagerModule {
@Singleton
fun provideFeatureTogglesManager(
@ApplicationContext context: Context,
- assetLoader: AssetLoader,
appPreferencesStore: AppPreferencesStore,
): FeatureTogglesManager {
- val localTogglesStorage = LocalTogglesStorage(assetLoader)
val versionProvider = DefaultVersionProvider(context)
return if (BuildConfig.TESTER_MENU_ENABLED) {
DevFeatureTogglesManager(
- localTogglesStorage = localTogglesStorage,
- appPreferencesStore = appPreferencesStore,
versionProvider = versionProvider,
+ featureTogglesLocalStorage = FeatureTogglesLocalStorage(appPreferencesStore),
)
} else {
- ProdFeatureTogglesManager(
- localTogglesStorage = localTogglesStorage,
- versionProvider = versionProvider,
- )
- }.also {
- // We need to initialize during the hilt graph creation
- // in order to provide the feature toggles correctly to other dependencies.
- runBlocking {
- it.init()
- }
+ ProdFeatureTogglesManager(versionProvider = versionProvider)
}
}
}
\ No newline at end of file
diff --git a/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/feature/FeatureTogglesManager.kt b/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/feature/FeatureTogglesManager.kt
index 86a027924c..0296565727 100644
--- a/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/feature/FeatureTogglesManager.kt
+++ b/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/feature/FeatureTogglesManager.kt
@@ -7,9 +7,6 @@ package com.tangem.core.configtoggle.feature
*/
interface FeatureTogglesManager {
- /** Initialize manager */
- suspend fun init()
-
/** Check feature toggle availability by name [name] */
fun isFeatureEnabled(name: String): Boolean
}
\ No newline at end of file
diff --git a/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/feature/impl/DevFeatureTogglesManager.kt b/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/feature/impl/DevFeatureTogglesManager.kt
index ce0d035439..6677cae6d0 100644
--- a/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/feature/impl/DevFeatureTogglesManager.kt
+++ b/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/feature/impl/DevFeatureTogglesManager.kt
@@ -1,77 +1,77 @@
package com.tangem.core.configtoggle.feature.impl
import androidx.annotation.VisibleForTesting
+import com.tangem.core.configtoggle.FeatureToggles
import com.tangem.core.configtoggle.feature.MutableFeatureTogglesManager
-import com.tangem.core.configtoggle.storage.TogglesStorage
-import com.tangem.core.configtoggle.utils.associateToggles
+import com.tangem.core.configtoggle.storage.FeatureTogglesLocalStorage
+import com.tangem.core.configtoggle.utils.defineTogglesAvailability
import com.tangem.core.configtoggle.version.VersionProvider
-import com.tangem.datasource.local.preferences.AppPreferencesStore
-import com.tangem.datasource.local.preferences.PreferencesKeys
-import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
-import com.tangem.datasource.local.preferences.utils.storeObject
+import kotlinx.coroutines.runBlocking
+import java.util.Locale
+import kotlin.properties.Delegates
/**
- * Feature toggles manager implementation in DEV build
+ * Feature toggles manager implementation in dev or mocked build
*
- * @property localTogglesStorage local feature toggles storage
- * @property appPreferencesStore application local store
* @property versionProvider application version provider
+ * @property featureTogglesLocalStorage local storage for feature toggles
*/
internal class DevFeatureTogglesManager(
- private val localTogglesStorage: TogglesStorage,
- private val appPreferencesStore: AppPreferencesStore,
private val versionProvider: VersionProvider,
+ private val featureTogglesLocalStorage: FeatureTogglesLocalStorage,
) : MutableFeatureTogglesManager {
- private var featureTogglesMap: MutableMap? = null
- private var localFeatureTogglesMap: Map? = null
+ private var fileFeatureTogglesMap: Map = getFileFeatureToggles()
+ private var featureTogglesMap: MutableMap by Delegates.notNull()
- override suspend fun init() {
- if (featureTogglesMap != null && localFeatureTogglesMap != null) {
- return // Already initialized
- }
+ init {
+ val savedFeatureToggles = runBlocking { featureTogglesLocalStorage.getSyncOrEmpty() }
- localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
-
- val savedFeatureToggles = appPreferencesStore.getObjectSyncOrNull