diff --git a/v4/app/src/main/java/exchange/dydx/trading/AppModule.kt b/v4/app/src/main/java/exchange/dydx/trading/AppModule.kt index 5856b68e..2fea3019 100644 --- a/v4/app/src/main/java/exchange/dydx/trading/AppModule.kt +++ b/v4/app/src/main/java/exchange/dydx/trading/AppModule.kt @@ -1,7 +1,9 @@ package exchange.dydx.trading +import android.app.Application import android.content.Context import androidx.compose.material.SnackbarHostState +import dagger.Binds import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -21,6 +23,7 @@ import exchange.dydx.abacus.utils.IOImplementations import exchange.dydx.abacus.utils.Parser import exchange.dydx.dydxstatemanager.AbacusStateManager import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.dydxstatemanager.EnvKey import exchange.dydx.dydxstatemanager.clientState.DydxClientState import exchange.dydx.dydxstatemanager.clientState.favorite.DydxFavoriteStore import exchange.dydx.dydxstatemanager.clientState.favorite.DydxFavoriteStoreProtocol @@ -36,6 +39,7 @@ import exchange.dydx.dydxstatemanager.protocolImplementations.AbacusThreadingImp import exchange.dydx.dydxstatemanager.protocolImplementations.AbacusTimerImp import exchange.dydx.dydxstatemanager.protocolImplementations.AbacusTrackingImp import exchange.dydx.dydxstatemanager.protocolImplementations.AbacusWebSocketImp +import exchange.dydx.dydxstatemanager.protocolImplementations.LanguageKey import exchange.dydx.platformui.components.PlatformDialog import exchange.dydx.platformui.components.PlatformInfo import exchange.dydx.platformui.designSystem.theme.StyleConfig @@ -64,235 +68,127 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -object AppModule { - @Provides - @Singleton - fun provideThemeSettings( - @ApplicationContext appContext: Context, - sharedPreferencesStore: SharedPreferencesStore, - ): ThemeSettings { - val preferenceStore = provideSharedPreferencesStore(appContext) - var theme = preferenceStore.read(PreferenceKeys.Theme) - if (theme.isNullOrEmpty()) { - theme = "dark" +interface AppModule { + + companion object { + @Provides + @Singleton + fun provideThemeSettings( + @ApplicationContext appContext: Context, + preferenceStore: SharedPreferencesStore, + ): ThemeSettings { + var theme = preferenceStore.read(PreferenceKeys.Theme) + if (theme.isNullOrEmpty()) { + theme = "dark" + } + val themeConfigValue = + ThemeConfig.createFromPreference(appContext, theme) ?: ThemeConfig.dark(appContext) + val themeConfig = MutableStateFlow(themeConfigValue) + val styleConfig = + MutableStateFlow(JsonUtils.loadFromAssets(appContext, "dydxStyle.json")) + ThemeSettings.shared = + ThemeSettings(appContext, preferenceStore, themeConfig, styleConfig) + return ThemeSettings.shared } - val themeConfigValue = - ThemeConfig.createFromPreference(appContext, theme) ?: ThemeConfig.dark(appContext) - val themeConfig = MutableStateFlow(themeConfigValue) - val styleConfig = - MutableStateFlow(JsonUtils.loadFromAssets(appContext, "dydxStyle.json")) - ThemeSettings.shared = - ThemeSettings(appContext, sharedPreferencesStore, themeConfig, styleConfig) - return ThemeSettings.shared - } - - @Provides - @Singleton - fun provideParser(): ParserProtocol = - Parser() - - @Provides - @Singleton - fun provideSharedPreferencesStore(@ApplicationContext appContext: Context): SharedPreferencesStore = - SharedPreferencesStore(appContext) - - @Provides - @Singleton - fun provideSecureStore(@ApplicationContext appContext: Context): SecureStore = - SecureStore(appContext) - - @Provides - @Singleton - fun provideClientState( - sharedPreferencesStore: SharedPreferencesStore, - secureStore: SecureStore, - ): DydxClientState = - DydxClientState(sharedPreferencesStore, secureStore) - - @Provides - @Singleton - fun provideCosmosClient(client: CosmosV4WebviewClientProtocol): CosmosV4ClientProtocol = - client - - @Provides - @Singleton - fun provideCosmosWebviewClient(@ApplicationContext appContext: Context): CosmosV4WebviewClientProtocol = - CosmosV4ClientWebview(appContext) - - @Provides - @Singleton - fun provideRest(): RestProtocol = - AbacusRestImp() - - @Provides - @Singleton - fun provideWebsocket(): WebSocketProtocol = - AbacusWebSocketImp() - - @Provides - @Singleton - fun provideChain(cosmosClient: CosmosV4ClientProtocol): DYDXChainTransactionsProtocol = - AbacusChainImp(cosmosClient) - - @Provides - @Singleton - fun provideTrackingProtocol( - tracker: Tracking, - ): TrackingProtocol = - tracker - @Provides - @Singleton - fun provideTracking( - tracker: CompositeTracking, - ): Tracking = - AbacusTrackingImp(tracker) + @Provides @Singleton fun provideParser(): ParserProtocol = Parser() + + @Provides + @Singleton + fun provideIOImplementation( + rest: RestProtocol?, + webSocket: WebSocketProtocol?, + chain: DYDXChainTransactionsProtocol?, + tracking: TrackingProtocol?, + threading: ThreadingProtocol?, + timer: TimerProtocol?, + fileSystem: FileSystemProtocol?, + ): IOImplementations = + IOImplementations(rest, webSocket, chain, tracking, threading, timer, fileSystem) + + @EnvKey @Provides fun provideEnvKey(): String = PreferenceKeys.Env + + @LanguageKey @Provides fun provideLanguageKey(): String = PreferenceKeys.Language + + @Provides + fun providePlatformInfo(): PlatformInfo = + PlatformInfo( + snackbarHostState = SnackbarHostState(), + infoType = MutableStateFlow(PlatformInfo.InfoType.Info), + ) + + @Provides + fun providePlatformDialog( + localizer: LocalizerProtocol, + ): PlatformDialog = + PlatformDialog( + cancelTitle = localizer.localize("APP.GENERAL.CANCEL"), + confirmTitle = localizer.localize("APP.GENERAL.OK"), + ) + + @Provides + fun provideAppConfig( + application: Application, + ): AppConfig = AppConfigImpl( + appContext = application, + appVersionName = BuildConfig.VERSION_NAME, + appVersionCode = BuildConfig.VERSION_CODE.toString(), + debug = BuildConfig.DEBUG, + activityClass = TradingActivity::class.java, + ) - @Provides - @Singleton - fun provideCompositeTracker(): CompositeTracking = - CompositeTracker() + @Provides + @Singleton + fun provideJson(appConfig: AppConfig): Json = Json { + this.prettyPrint = appConfig.debug + this.ignoreUnknownKeys = true // !appConfig.debug + } - @Provides - @Singleton - fun provideThreading(): ThreadingProtocol = - AbacusThreadingImp() + @Provides + fun provideTheme( + application: Application, + appConfig: AppConfig, + ): DydxTheme { + return DydxThemeImpl(application, appConfig, true) + } + } - @Provides - @Singleton - fun provideTimer(): TimerProtocol = - AbacusTimerImp() + @Binds fun bindCosmosClient(client: CosmosV4WebviewClientProtocol): CosmosV4ClientProtocol - @Provides - @Singleton - fun provideFileSystem(@ApplicationContext appContext: Context): FileSystemProtocol = - AbacusFileSystemImp(appContext) + @Binds fun bindRest(abacusRestImp: AbacusRestImp): RestProtocol - @Provides - @Singleton - fun provideIOImplementation( - rest: RestProtocol?, - webSocket: WebSocketProtocol?, - chain: DYDXChainTransactionsProtocol?, - tracking: TrackingProtocol?, - threading: ThreadingProtocol?, - timer: TimerProtocol?, - fileSystem: FileSystemProtocol?, - ): IOImplementations = - IOImplementations(rest, webSocket, chain, tracking, threading, timer, fileSystem) + @Binds fun bindWebsocket(abacusWebSocketImp: AbacusWebSocketImp): WebSocketProtocol - @Provides - @Singleton - fun provideAbacusStateManager( - @ApplicationContext appContext: Context, - ioImplementations: IOImplementations, - parser: ParserProtocol, - walletStateManager: DydxWalletStateManagerProtocol, - transferStateManager: DydxTransferStateManagerProtocol, - cosmosClient: CosmosV4ClientProtocol, - preferencesStore: SharedPreferencesStore, - featureFlags: DydxFeatureFlags, - ): AbacusStateManagerProtocol = - AbacusStateManager( - appContext, - ioImplementations, - parser, - walletStateManager, - transferStateManager, - cosmosClient, - preferencesStore, - PreferenceKeys.Env, - featureFlags, - ) + @Binds fun bindChainProtocol(abacusChainImp: AbacusChainImp): DYDXChainTransactionsProtocol - @Provides - @Singleton - fun provideFeatureFlags(preferencesStore: SharedPreferencesStore): DydxFeatureFlags = - DydxFeatureFlags(preferencesStore) + @Binds fun bindTrackingProtocol(abacusTrackingImp: AbacusTrackingImp): TrackingProtocol - @Provides - @Singleton - fun provideLocalizer( - abacusLocalizerProtocol: AbacusLocalizerProtocol, - ): LocalizerProtocol = - abacusLocalizerProtocol + @Binds fun bindTracking(abacusTrackingImp: AbacusTrackingImp): Tracking - @Provides - @Singleton - fun provideMutableLocalizer( - preferencesStore: SharedPreferencesStore, - ioImplementations: IOImplementations, - ): AbacusLocalizerProtocol = - AbacusLocalizerImp(preferencesStore, PreferenceKeys.Language, ioImplementations) + @Binds fun bindThreading(abacusThreadingImp: AbacusThreadingImp): ThreadingProtocol - @Provides - @Singleton - fun provideWalletStateManager(clientState: DydxClientState): DydxWalletStateManagerProtocol = - DydxWalletStateManager(clientState) + @Binds fun bindTimer(abacusTimerImp: AbacusTimerImp): TimerProtocol - @Provides - @Singleton - fun provideTransferStateManager(clientState: DydxClientState): DydxTransferStateManagerProtocol = - DydxTransferStateManager(clientState) + @Binds fun bindFileSystem(abacusFileSystemImp: AbacusFileSystemImp): FileSystemProtocol - @Provides - @Singleton - fun provideUserFavoriteStore(clientState: DydxClientState): DydxFavoriteStoreProtocol = - DydxFavoriteStore(clientState) + @Binds fun bindAbacusStateManager(abacusStateManager: AbacusStateManager): AbacusStateManagerProtocol - @Provides - fun providePlatformInfo(): PlatformInfo = - PlatformInfo( - snackbarHostState = SnackbarHostState(), - infoType = MutableStateFlow(PlatformInfo.InfoType.Info), - ) + @Binds fun bindLocalizer(abacusLocalizerProtocol: AbacusLocalizerProtocol): LocalizerProtocol - @Provides - fun providePlatformDialog( - localizer: LocalizerProtocol, - ): PlatformDialog = - PlatformDialog( - cancelTitle = localizer.localize("APP.GENERAL.CANCEL"), - confirmTitle = localizer.localize("APP.GENERAL.OK"), - ) + @Binds + fun bindLocalizerProtocol(abacusLocalizerImp: AbacusLocalizerImp): AbacusLocalizerProtocol - @Provides - @Singleton - fun provideCachedFileLoader( - @ApplicationContext appContext: Context, - ): CachedFileLoader { - return CachedFileLoader(appContext) - } + @Binds + fun bindWalletStateManagerProtocol(walletStateManager: DydxWalletStateManager): DydxWalletStateManagerProtocol - @Provides - @Singleton - fun provideAppConfig( - @ApplicationContext appContext: Context, - ): AppConfig = AppConfigImpl( - appContext = appContext, - appVersionName = BuildConfig.VERSION_NAME, - appVersionCode = BuildConfig.VERSION_CODE.toString(), - debug = BuildConfig.DEBUG, - activityClass = TradingActivity::class.java, - ) + @Binds + fun bindTransferStateManagerProtocol(dydxTransferStateManager: DydxTransferStateManager): DydxTransferStateManagerProtocol - @Provides - @Singleton - fun provideLogger(): DydxLogger = DydxLogger() + @Binds + fun bindUserFavoriteStoreProtocol(dydxFavoriteStore: DydxFavoriteStore): DydxFavoriteStoreProtocol - @Provides - @Singleton - fun provideJson(appConfig: AppConfig): Json = Json { - this.prettyPrint = appConfig.debug - this.ignoreUnknownKeys = true // !appConfig.debug - } + @Binds + fun bindCompositeTracking(compositeTracker: CompositeTracker): CompositeTracking - @Provides - @Singleton - fun provideTheme( - @ApplicationContext appContext: Context, - appConfig: AppConfig, - ): DydxTheme { - return DydxThemeImpl(appContext, appConfig, true) - } + @Binds fun bindCosmosV4WebviewClientProtocol(cosmosV4ClientWebview: CosmosV4ClientWebview): CosmosV4WebviewClientProtocol } diff --git a/v4/common/src/main/java/exchange/dydx/trading/common/AppConfig.kt b/v4/common/src/main/java/exchange/dydx/trading/common/AppConfig.kt index 58391445..b7c9b095 100644 --- a/v4/common/src/main/java/exchange/dydx/trading/common/AppConfig.kt +++ b/v4/common/src/main/java/exchange/dydx/trading/common/AppConfig.kt @@ -1,5 +1,6 @@ package exchange.dydx.trading.common +import android.app.Application import android.content.Context import android.util.Log import androidx.compose.runtime.Composable @@ -35,7 +36,7 @@ interface AppConfig { } data class AppConfigImpl( - override val appContext: Context?, + override val appContext: Application?, override val appVersionName: String, override val appVersionCode: String, override val debug: Boolean, diff --git a/v4/common/src/main/java/exchange/dydx/trading/common/featureflags/DydxFeatureFlags.kt b/v4/common/src/main/java/exchange/dydx/trading/common/featureflags/DydxFeatureFlags.kt index 99c669e7..0792916c 100644 --- a/v4/common/src/main/java/exchange/dydx/trading/common/featureflags/DydxFeatureFlags.kt +++ b/v4/common/src/main/java/exchange/dydx/trading/common/featureflags/DydxFeatureFlags.kt @@ -1,13 +1,14 @@ package exchange.dydx.trading.common.featureflags import exchange.dydx.utilities.utils.SharedPreferencesStore +import javax.inject.Inject enum class DydxFeatureFlag { deployment_url, force_mainnet, } -class DydxFeatureFlags( +class DydxFeatureFlags @Inject constructor( private val sharedPreferences: SharedPreferencesStore ) { fun isFeatureEnabled(featureFlag: DydxFeatureFlag): Boolean { diff --git a/v4/common/src/main/java/exchange/dydx/trading/common/logger/DydxLogger.kt b/v4/common/src/main/java/exchange/dydx/trading/common/logger/DydxLogger.kt index 7a757b71..1e8620ff 100644 --- a/v4/common/src/main/java/exchange/dydx/trading/common/logger/DydxLogger.kt +++ b/v4/common/src/main/java/exchange/dydx/trading/common/logger/DydxLogger.kt @@ -10,6 +10,8 @@ import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST import kotlinx.coroutines.flow.MutableSharedFlow import timber.log.Timber import timber.log.Timber.Tree +import javax.inject.Inject +import javax.inject.Singleton private const val TAG = "DydxLogger" class DydxTimberTree : Timber.DebugTree() { @@ -28,7 +30,8 @@ class DydxTimberTree : Timber.DebugTree() { } } -class DydxLogger { +@Singleton +class DydxLogger @Inject constructor() { val debugTree: DydxTimberTree = DydxTimberTree() private var woodTree: WoodTree? = null fun woodTree(application: Application): Tree { diff --git a/v4/common/src/main/java/exchange/dydx/trading/common/theme/DydxTheme.kt b/v4/common/src/main/java/exchange/dydx/trading/common/theme/DydxTheme.kt index 0e7ca0bb..c231f691 100644 --- a/v4/common/src/main/java/exchange/dydx/trading/common/theme/DydxTheme.kt +++ b/v4/common/src/main/java/exchange/dydx/trading/common/theme/DydxTheme.kt @@ -66,7 +66,7 @@ fun DydxThemedPreviewSurface( val themeConfig = MutableStateFlow(theme) val styleConfig = MutableStateFlow(JsonUtils.loadFromAssets(appContext, "dydxStyle.json")) - ThemeSettings.shared = ThemeSettings(appContext, SharedPreferencesStore(appContext), themeConfig, styleConfig) + ThemeSettings.shared = ThemeSettings(appContext, null, themeConfig, styleConfig) Surface( color = background.color, diff --git a/v4/integration/analytics/src/main/java/exchange/dydx/trading/integration/analytics/CompositeTracker.kt b/v4/integration/analytics/src/main/java/exchange/dydx/trading/integration/analytics/CompositeTracker.kt index f2feddbb..9db3b12b 100644 --- a/v4/integration/analytics/src/main/java/exchange/dydx/trading/integration/analytics/CompositeTracker.kt +++ b/v4/integration/analytics/src/main/java/exchange/dydx/trading/integration/analytics/CompositeTracker.kt @@ -1,6 +1,10 @@ package exchange.dydx.trading.integration.analytics -class CompositeTracker : CompositeTracking { +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class CompositeTracker @Inject constructor() : CompositeTracking { private val trackers = mutableListOf() override fun addTracker(tracker: Tracking) { diff --git a/v4/integration/cosmos/src/main/java/exchange/dydx/trading/integration/cosmos/CosmosV4ClientWebview.kt b/v4/integration/cosmos/src/main/java/exchange/dydx/trading/integration/cosmos/CosmosV4ClientWebview.kt index aacfd9af..e715536a 100644 --- a/v4/integration/cosmos/src/main/java/exchange/dydx/trading/integration/cosmos/CosmosV4ClientWebview.kt +++ b/v4/integration/cosmos/src/main/java/exchange/dydx/trading/integration/cosmos/CosmosV4ClientWebview.kt @@ -1,21 +1,26 @@ package exchange.dydx.trading.integration.cosmos +import android.app.Application import android.content.Context import exchange.dydx.integration.javascript.JavascriptApiImpl import exchange.dydx.integration.javascript.JavascriptRunnerV4 import kotlinx.coroutines.runBlocking import java.io.IOException import java.util.Locale +import javax.inject.Inject +import javax.inject.Singleton -class CosmosV4ClientWebview( - context: Context, - filename: String = "v4-native-client.js", +private const val WEBVIEW_FILENAME = "v4-native-client.js" + +@Singleton +class CosmosV4ClientWebview @Inject constructor( + application: Application, ) : CosmosV4WebviewClientProtocol, JavascriptApiImpl( - context = context, - description = filename, - runner = JavascriptRunnerV4.runnerFromFile(context, filename) - ?: throw IOException("Fatal, unable to load runner from: $filename"), + context = application, + description = WEBVIEW_FILENAME, + runner = JavascriptRunnerV4.runnerFromFile(application, WEBVIEW_FILENAME) + ?: throw IOException("Fatal, unable to load runner from: $WEBVIEW_FILENAME"), ) { override val initialized = runner.initialized diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt index 4de8165e..2ca1f9d8 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt @@ -1,5 +1,6 @@ package exchange.dydx.dydxstatemanager +import android.app.Application import android.content.Context import android.net.ConnectivityManager import android.net.Network @@ -49,6 +50,8 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import javax.inject.Inject +import javax.inject.Qualifier +import javax.inject.Singleton interface AbacusStateManagerProtocol { @@ -107,15 +110,19 @@ interface AbacusStateManagerProtocol { } } +// Temporary location, should probably make a separate dagger-qualifiers module. +@Qualifier annotation class EnvKey + +@Singleton class AbacusStateManager @Inject constructor( - private val context: Context, + private val application: Application, private val ioImplementations: IOImplementations, private val parser: ParserProtocol, private val walletStateManager: DydxWalletStateManagerProtocol, private val transferStateManager: DydxTransferStateManagerProtocol, private val cosmosClient: CosmosV4ClientProtocol, private val preferencesStore: SharedPreferencesStore, - private val envKey: String, + @EnvKey private val envKey: String, private val featureFlags: DydxFeatureFlags, ) : AbacusStateManagerProtocol, StateNotificationProtocol { @@ -135,7 +142,7 @@ class AbacusStateManager @Inject constructor( deployment = "MAINNET" appConfigs = AppConfigs.forApp } else { - deployment = context.getString(R.string.app_deployment) + deployment = application.getString(R.string.app_deployment) appConfigs = if (BuildConfig.DEBUG && deployment != "MAINNET") AppConfigs.forAppDebug else AppConfigs.forApp } @@ -172,7 +179,7 @@ class AbacusStateManager @Inject constructor( if (!urlOverride.isNullOrEmpty()) { return urlOverride } else { - return "https://" + context.getString(R.string.app_web_host) + return "https://" + application.getString(R.string.app_web_host) } } @@ -191,7 +198,7 @@ class AbacusStateManager @Inject constructor( init { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - this.monitorConnectivity(context) + this.monitorConnectivity(application) } } diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/DydxClientState.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/DydxClientState.kt index ee7308a6..14dd14e3 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/DydxClientState.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/DydxClientState.kt @@ -6,7 +6,9 @@ import exchange.dydx.utilities.utils.StoreProtocol import exchange.dydx.utilities.utils.read import exchange.dydx.utilities.utils.save import javax.inject.Inject +import javax.inject.Singleton +@Singleton class DydxClientState @Inject constructor( private val sharedPreferencesStore: SharedPreferencesStore, private val secureStore: SecureStore, diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/favorite/DydxFavoriteStore.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/favorite/DydxFavoriteStore.kt index b0cc513d..fdcb8b9b 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/favorite/DydxFavoriteStore.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/favorite/DydxFavoriteStore.kt @@ -5,6 +5,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.serialization.Serializable import javax.inject.Inject +import javax.inject.Singleton interface DydxFavoriteStoreProtocol { val state: Flow @@ -14,6 +15,7 @@ interface DydxFavoriteStoreProtocol { fun clear() } +@Singleton class DydxFavoriteStore @Inject constructor( private val clientState: DydxClientState, ) : DydxFavoriteStoreProtocol { diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/transfers/DydxTransferState.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/transfers/DydxTransferState.kt index 483bb934..e15a8469 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/transfers/DydxTransferState.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/transfers/DydxTransferState.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.serialization.Serializable import java.time.Instant import javax.inject.Inject +import javax.inject.Singleton interface DydxTransferStateManagerProtocol { val state: Flow @@ -16,6 +17,7 @@ interface DydxTransferStateManagerProtocol { fun clear() } +@Singleton class DydxTransferStateManager @Inject constructor( private val clientState: DydxClientState, ) : DydxTransferStateManagerProtocol { diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/wallets/DydxWalletState.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/wallets/DydxWalletState.kt index 68cd38d8..23464f1e 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/wallets/DydxWalletState.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/clientState/wallets/DydxWalletState.kt @@ -7,6 +7,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.serialization.Serializable import javax.inject.Inject +import javax.inject.Singleton interface DydxWalletStateManagerProtocol { val state: StateFlow @@ -15,6 +16,7 @@ interface DydxWalletStateManagerProtocol { fun replaceWallet() } +@Singleton class DydxWalletStateManager @Inject constructor( private val clientState: DydxClientState, ) : DydxWalletStateManagerProtocol { diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusChainImp.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusChainImp.kt index 550ff104..f2a6c2b0 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusChainImp.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusChainImp.kt @@ -4,8 +4,9 @@ import exchange.dydx.abacus.protocols.DYDXChainTransactionsProtocol import exchange.dydx.abacus.protocols.QueryType import exchange.dydx.abacus.protocols.TransactionType import exchange.dydx.trading.integration.cosmos.CosmosV4ClientProtocol +import javax.inject.Inject -class AbacusChainImp( +class AbacusChainImp @Inject constructor( private val cosmosClient: CosmosV4ClientProtocol, ) : DYDXChainTransactionsProtocol { diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusFileSystemImp.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusFileSystemImp.kt index b520177c..600beb20 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusFileSystemImp.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusFileSystemImp.kt @@ -1,5 +1,6 @@ package exchange.dydx.dydxstatemanager.protocolImplementations +import android.app.Application import android.content.Context import android.util.Log import exchange.dydx.abacus.protocols.FileLocation @@ -8,9 +9,10 @@ import okio.use import timber.log.Timber import java.io.File import java.io.FileOutputStream +import javax.inject.Inject -class AbacusFileSystemImp( - private val context: Context?, +class AbacusFileSystemImp @Inject constructor( + private val application: Application, ) : FileSystemProtocol { private val TAG = "AbacusFileSystemImp" @@ -18,10 +20,8 @@ class AbacusFileSystemImp( val path = if (path.first() == '/') path.drop(1) else path when (location) { FileLocation.AppBundle -> { - val context = context - return if (context != null) { - try { - val string = context.assets.open(path).bufferedReader().use { + return try { + val string = application.assets.open(path).bufferedReader().use { it.readText() } string @@ -29,24 +29,15 @@ class AbacusFileSystemImp( Timber.tag(TAG).e(e.stackTraceToString()) null } - } else { - Timber.tag(TAG).e("Context is null") - null - } } FileLocation.AppDocs -> { - val context = context - return if (context != null) { - val absoluteFilePath = File(context.filesDir, path).absolutePath - val file = File(absoluteFilePath) - if (file.exists()) { - file.readText() - } else { - Timber.tag(TAG).e("File does not exist: %s", absoluteFilePath) - null - } + val absoluteFilePath = File(application.filesDir, path).absolutePath + val file = File(absoluteFilePath) + + return if (file.exists()) { + file.readText() } else { - Timber.tag(TAG).e("Context is null") + Timber.tag(TAG).e("File does not exist: %s", absoluteFilePath) null } } @@ -54,31 +45,25 @@ class AbacusFileSystemImp( } override fun writeTextFile(path: String, text: String): Boolean { - val context = context - if (context != null) { - val path = if (path.first() == '/') path.drop(1) else path - val absoluteFilePath = File(context.filesDir, path).absolutePath + val path = if (path.first() == '/') path.drop(1) else path + val absoluteFilePath = File(application.filesDir, path).absolutePath - // Create parent directories if they don't exist - val parentDir = File(absoluteFilePath).parentFile - if (parentDir != null) { - if (!parentDir.exists()) { - parentDir.mkdirs() - } + // Create parent directories if they don't exist + val parentDir = File(absoluteFilePath).parentFile + if (parentDir != null) { + if (!parentDir.exists()) { + parentDir.mkdirs() } + } - try { - val fos = FileOutputStream(absoluteFilePath) - fos.write(text.toByteArray()) - fos.close() - } catch (e: Exception) { - e.printStackTrace() - return false - } - return true - } else { - Log.e(TAG, "Context is null") + try { + val fos = FileOutputStream(absoluteFilePath) + fos.write(text.toByteArray()) + fos.close() + } catch (e: Exception) { + e.printStackTrace() return false } + return true } } diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusLocalizerImp.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusLocalizerImp.kt index 6facb0c7..f0d55186 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusLocalizerImp.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusLocalizerImp.kt @@ -7,10 +7,19 @@ import exchange.dydx.abacus.responses.ParsingError import exchange.dydx.abacus.state.app.helper.DynamicLocalizer import exchange.dydx.abacus.utils.IOImplementations import exchange.dydx.utilities.utils.SharedPreferencesStore +import javax.inject.Inject +import javax.inject.Qualifier +import javax.inject.Singleton -class AbacusLocalizerImp( +// Temporary location, should probably make a separate dagger-qualifiers module. +@Qualifier annotation class LanguageKey + +// While this class doesn't technically hold any state, it does have a side-effect on init. +// So, scoping this as a singleton. +@Singleton +class AbacusLocalizerImp @Inject constructor( private val sharedPreferencesStore: SharedPreferencesStore, - private val preferenceKey: String, + @LanguageKey private val preferenceKey: String, private val ioImplementations: IOImplementations, ) : AbacusLocalizerProtocol { diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusRestImp.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusRestImp.kt index 1fa4f61c..7d4220c7 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusRestImp.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusRestImp.kt @@ -13,8 +13,11 @@ import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import java.io.IOException import java.util.concurrent.TimeUnit +import javax.inject.Inject +import javax.inject.Singleton -class AbacusRestImp : RestProtocol { +@Singleton +class AbacusRestImp @Inject constructor() : RestProtocol { private val TAG = "AbacusRestImp" diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusThreadingImp.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusThreadingImp.kt index 21d96ebc..a0ffbc1b 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusThreadingImp.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusThreadingImp.kt @@ -7,8 +7,11 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.newSingleThreadContext import kotlinx.coroutines.plus +import javax.inject.Inject +import javax.inject.Singleton -class AbacusThreadingImp : ThreadingProtocol { +@Singleton +class AbacusThreadingImp @Inject constructor() : ThreadingProtocol { private val mainScope = CoroutineScope(Dispatchers.Main) // + Job()) private val abacusScope = CoroutineScope(newSingleThreadContext("AbacusScope")) private val networkScope = CoroutineScope(Dispatchers.IO) diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusTimerImp.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusTimerImp.kt index 69ba5c88..1457a381 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusTimerImp.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusTimerImp.kt @@ -4,8 +4,9 @@ import exchange.dydx.abacus.protocols.LocalTimerProtocol import exchange.dydx.abacus.protocols.TimerProtocol import java.util.Timer import java.util.TimerTask +import javax.inject.Inject -class AbacusTimerImp : TimerProtocol { +class AbacusTimerImp @Inject constructor(): TimerProtocol { override fun schedule( delay: Double, repeat: Double?, diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusTrackingImp.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusTrackingImp.kt index f6d651ef..8b527e1f 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusTrackingImp.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusTrackingImp.kt @@ -1,13 +1,15 @@ package exchange.dydx.dydxstatemanager.protocolImplementations import exchange.dydx.abacus.protocols.TrackingProtocol +import exchange.dydx.trading.integration.analytics.CompositeTracking import exchange.dydx.trading.integration.analytics.Tracking +import javax.inject.Inject -class AbacusTrackingImp( - private val nativeTracker: Tracking, +class AbacusTrackingImp @Inject constructor( + private val compositeTracking: CompositeTracking, ) : TrackingProtocol, Tracking { override fun log(event: String, data: String?) { - nativeTracker.log(event, data) + compositeTracking.log(event, data) } } diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusWebSocketImp.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusWebSocketImp.kt index ebbbee1e..99069453 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusWebSocketImp.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusWebSocketImp.kt @@ -7,8 +7,11 @@ import okhttp3.WebSocket import okhttp3.WebSocketListener import okio.ByteString import java.util.concurrent.TimeUnit +import javax.inject.Inject +import javax.inject.Singleton -class AbacusWebSocketImp : exchange.dydx.abacus.protocols.WebSocketProtocol { +@Singleton +class AbacusWebSocketImp @Inject constructor() : exchange.dydx.abacus.protocols.WebSocketProtocol { private var url: String? = null private var connected: ((Boolean) -> Unit)? = null diff --git a/v4/platformUI/src/main/java/exchange/dydx/platformui/designSystem/theme/ThemeConfig.kt b/v4/platformUI/src/main/java/exchange/dydx/platformui/designSystem/theme/ThemeConfig.kt index 84feb30f..1ea60bb0 100644 --- a/v4/platformUI/src/main/java/exchange/dydx/platformui/designSystem/theme/ThemeConfig.kt +++ b/v4/platformUI/src/main/java/exchange/dydx/platformui/designSystem/theme/ThemeConfig.kt @@ -9,7 +9,7 @@ import kotlinx.serialization.Serializable class ThemeSettings( private val context: Context, - val sharedPreferences: SharedPreferencesStore, + val sharedPreferences: SharedPreferencesStore?, val themeConfig: MutableStateFlow = MutableStateFlow(ThemeConfig.sampleThemeConfig(context)), val styleConfig: MutableStateFlow = MutableStateFlow(StyleConfig.sampleStyleConfig(context)), ) { diff --git a/v4/platformUI/src/main/java/exchange/dydx/platformui/designSystem/theme/ThemeSettingsExt.kt b/v4/platformUI/src/main/java/exchange/dydx/platformui/designSystem/theme/ThemeSettingsExt.kt index aaf5a569..7d1ba384 100644 --- a/v4/platformUI/src/main/java/exchange/dydx/platformui/designSystem/theme/ThemeSettingsExt.kt +++ b/v4/platformUI/src/main/java/exchange/dydx/platformui/designSystem/theme/ThemeSettingsExt.kt @@ -128,14 +128,18 @@ private fun family( } val ThemeColor.SemanticColor.Companion.positiveColor: ThemeColor.SemanticColor - get() = if (ThemeSettings.shared.sharedPreferences.read("direction_color_preference", "green_is_up") == "green_is_up") { + get() = if ((ThemeSettings.shared.sharedPreferences?.read("direction_color_preference") + ?: "green_is_up") == "green_is_up" + ) { ThemeColor.SemanticColor.color_green } else { ThemeColor.SemanticColor.color_red } val ThemeColor.SemanticColor.Companion.negativeColor: ThemeColor.SemanticColor - get() = if (ThemeSettings.shared.sharedPreferences.read("direction_color_preference", "green_is_up") == "green_is_up") { + get() = if ((ThemeSettings.shared.sharedPreferences?.read("direction_color_preference") + ?: "green_is_up") == "green_is_up" + ) { ThemeColor.SemanticColor.color_red } else { ThemeColor.SemanticColor.color_green diff --git a/v4/utilities/build.gradle b/v4/utilities/build.gradle index 6bbfc1a7..13077f60 100644 --- a/v4/utilities/build.gradle +++ b/v4/utilities/build.gradle @@ -36,6 +36,8 @@ android { } dependencies { + api "com.google.dagger:hilt-android:$hiltVersion" + implementation "androidx.appcompat:appcompat:$appcompatVersion" implementation "androidx.compose.ui:ui-unit-android:$composeVersion" testImplementation "junit:junit:$junitVersion" diff --git a/v4/utilities/src/main/java/exchange/dydx/utilities/utils/CachedFileLoader.kt b/v4/utilities/src/main/java/exchange/dydx/utilities/utils/CachedFileLoader.kt index 49e3e6a7..1d19017b 100644 --- a/v4/utilities/src/main/java/exchange/dydx/utilities/utils/CachedFileLoader.kt +++ b/v4/utilities/src/main/java/exchange/dydx/utilities/utils/CachedFileLoader.kt @@ -1,14 +1,16 @@ package exchange.dydx.utilities.utils +import android.app.Application import android.content.Context import android.os.Handler import android.os.Looper import android.util.Log import java.io.File import java.net.URL +import javax.inject.Inject -class CachedFileLoader( - private val context: Context, +class CachedFileLoader @Inject constructor( + private val application: Application, ) { fun loadString(filePath: String, url: String?, completion: (String?) -> Unit) { loadData(filePath, url) { data -> @@ -23,7 +25,7 @@ class CachedFileLoader( if (cachedFile?.exists() == true) { completion(cachedFile.readBytes()) } else { - FileUtils.loadFromAssets(context, filePath)?.let { string -> + FileUtils.loadFromAssets(application, filePath)?.let { string -> completion(string.toByteArray()) } } @@ -57,6 +59,6 @@ class CachedFileLoader( } private fun cachedFilePath(filePath: String): File? { - return File(context.filesDir, filePath) + return File(application.filesDir, filePath) } } diff --git a/v4/utilities/src/main/java/exchange/dydx/utilities/utils/SecureStore.kt b/v4/utilities/src/main/java/exchange/dydx/utilities/utils/SecureStore.kt index 048330ef..189d7f64 100644 --- a/v4/utilities/src/main/java/exchange/dydx/utilities/utils/SecureStore.kt +++ b/v4/utilities/src/main/java/exchange/dydx/utilities/utils/SecureStore.kt @@ -1,12 +1,16 @@ package exchange.dydx.utilities.utils +import android.app.Application import android.content.Context import android.content.SharedPreferences import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKeys +import javax.inject.Inject +import javax.inject.Singleton -class SecureStore( - context: Context, +@Singleton +class SecureStore @Inject constructor( + application: Application, ) : StoreProtocol { companion object { private const val PREFERENCES_NAME = "SecureStorePrefs" @@ -18,7 +22,7 @@ class SecureStore( private val sharedPreferences: SharedPreferences = EncryptedSharedPreferences.create( PREFERENCES_NAME, masterKeyAlias, - context, + application, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM, ) diff --git a/v4/utilities/src/main/java/exchange/dydx/utilities/utils/SharedPreferencesStore.kt b/v4/utilities/src/main/java/exchange/dydx/utilities/utils/SharedPreferencesStore.kt index 3ff9af44..014962d9 100644 --- a/v4/utilities/src/main/java/exchange/dydx/utilities/utils/SharedPreferencesStore.kt +++ b/v4/utilities/src/main/java/exchange/dydx/utilities/utils/SharedPreferencesStore.kt @@ -1,10 +1,15 @@ package exchange.dydx.utilities.utils +import android.app.Application import android.content.Context import android.content.SharedPreferences +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject +import javax.inject.Singleton -class SharedPreferencesStore( - context: Context, +@Singleton +class SharedPreferencesStore @Inject constructor( + application: Application, ) : StoreProtocol { companion object { private const val PREFERENCES_NAME = "SharedPreferences" @@ -12,7 +17,7 @@ class SharedPreferencesStore( private const val DEFAULT_BOOLEAN_VALUE = false } - private val sharedPreferences: SharedPreferences = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) + private val sharedPreferences: SharedPreferences = application.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) override fun save(data: String, key: String) { sharedPreferences.edit().putString(key, data).apply()