From d41be27bbb95e9e8e4ce83b8248b0c0484ecdb9a Mon Sep 17 00:00:00 2001 From: Mikhail Date: Thu, 21 Apr 2022 16:25:53 +0300 Subject: [PATCH] App | Tech | Dagger Component instead of Subcomponent PoC (#2180) Co-authored-by: Mikhail Iudin --- .../anytype/app/AndroidApplication.kt | 10 +++- .../di/common/ComponentDependencies.kt | 17 ++++++ .../anytype/di/common/ComponentManager.kt | 21 +++++-- .../di/feature/settings/AppearanceDI.kt | 57 +++++++++++-------- .../anytype/di/main/MainComponent.kt | 20 ++++++- .../anytype/domain/theme/GetTheme.kt | 1 - ui-settings/build.gradle | 2 + .../appearance/AppearanceViewModel.kt | 3 +- .../ui_settings/appearance/ThemeApplicator.kt | 3 +- 9 files changed, 96 insertions(+), 38 deletions(-) create mode 100644 app/src/main/java/com/anytypeio/anytype/di/common/ComponentDependencies.kt diff --git a/app/src/main/java/com/anytypeio/anytype/app/AndroidApplication.kt b/app/src/main/java/com/anytypeio/anytype/app/AndroidApplication.kt index d4c92e7d20..ca11e298b3 100644 --- a/app/src/main/java/com/anytypeio/anytype/app/AndroidApplication.kt +++ b/app/src/main/java/com/anytypeio/anytype/app/AndroidApplication.kt @@ -9,7 +9,9 @@ import com.anytypeio.anytype.BuildConfig import com.anytypeio.anytype.R import com.anytypeio.anytype.analytics.tracker.AmplitudeTracker import com.anytypeio.anytype.core_utils.tools.CrashlyticsTree +import com.anytypeio.anytype.di.common.ComponentDependenciesProvider import com.anytypeio.anytype.di.common.ComponentManager +import com.anytypeio.anytype.di.common.HasComponentDependencies import com.anytypeio.anytype.di.main.ContextModule import com.anytypeio.anytype.di.main.DaggerMainComponent import com.anytypeio.anytype.di.main.MainComponent @@ -17,7 +19,7 @@ import com.anytypeio.anytype.middleware.interactor.LocalNetworkAddressHandler import timber.log.Timber import javax.inject.Inject -class AndroidApplication : Application() { +class AndroidApplication : Application(), HasComponentDependencies { @Inject lateinit var amplitudeTracker: AmplitudeTracker @@ -25,6 +27,10 @@ class AndroidApplication : Application() { @Inject lateinit var localNetworkAddressHandler: LocalNetworkAddressHandler + @Inject + override lateinit var dependencies: ComponentDependenciesProvider + protected set + private val main: MainComponent by lazy { DaggerMainComponent .builder() @@ -33,7 +39,7 @@ class AndroidApplication : Application() { } val componentManager by lazy { - ComponentManager(main) + ComponentManager(main, this) } override fun onCreate() { diff --git a/app/src/main/java/com/anytypeio/anytype/di/common/ComponentDependencies.kt b/app/src/main/java/com/anytypeio/anytype/di/common/ComponentDependencies.kt new file mode 100644 index 0000000000..f8fe67ac89 --- /dev/null +++ b/app/src/main/java/com/anytypeio/anytype/di/common/ComponentDependencies.kt @@ -0,0 +1,17 @@ +package com.anytypeio.anytype.di.common + +import dagger.MapKey +import kotlin.reflect.KClass + +interface ComponentDependencies + +typealias ComponentDependenciesProvider = Map, + @JvmSuppressWildcards ComponentDependencies> + +interface HasComponentDependencies { + val dependencies: ComponentDependenciesProvider +} + +@MapKey +@Target(AnnotationTarget.FUNCTION) +annotation class ComponentDependenciesKey(val value: KClass) \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/di/common/ComponentManager.kt b/app/src/main/java/com/anytypeio/anytype/di/common/ComponentManager.kt index a4f1dd4998..27807a0b21 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/common/ComponentManager.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/common/ComponentManager.kt @@ -13,13 +13,16 @@ import com.anytypeio.anytype.di.feature.sets.viewer.ViewerCardSizeSelectModule import com.anytypeio.anytype.di.feature.sets.viewer.ViewerImagePreviewSelectModule import com.anytypeio.anytype.di.feature.settings.AboutAppModule import com.anytypeio.anytype.di.feature.settings.AccountAndDataModule -import com.anytypeio.anytype.di.feature.settings.AppearanceModule +import com.anytypeio.anytype.di.feature.settings.DaggerAppearanceComponent import com.anytypeio.anytype.di.feature.settings.LogoutWarningModule import com.anytypeio.anytype.di.feature.settings.MainSettingsModule import com.anytypeio.anytype.di.feature.wallpaper.WallpaperSelectModule import com.anytypeio.anytype.di.main.MainComponent -class ComponentManager(private val main: MainComponent) { +class ComponentManager( + private val main: MainComponent, + private val provider: HasComponentDependencies +) { val mainEntryComponent = Component { main.mainEntryComponentBuilder().module(MainEntryModule).build() @@ -670,10 +673,6 @@ class ComponentManager(private val main: MainComponent) { main.accountAndDataComponent().module(AccountAndDataModule).build() } - val appearanceComponent = Component { - main.appearanceComponent().module(AppearanceModule).build() - } - val logoutWarningComponent = Component { main.logoutWarningComponent().module(LogoutWarningModule).build() } @@ -682,6 +681,12 @@ class ComponentManager(private val main: MainComponent) { main.mainSettingsComponent().module(MainSettingsModule).build() } + val appearanceComponent = Component { + DaggerAppearanceComponent + .factory() + .create(findComponentDependencies()) + } + class Component(private val builder: () -> T) { private var instance: T? = null @@ -720,4 +725,8 @@ class ComponentManager(private val main: MainComponent) { map.remove(id) } } + + private inline fun findComponentDependencies(): T { + return provider.dependencies[T::class.java] as T + } } \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AppearanceDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AppearanceDI.kt index 346e212acd..bad95eb4b7 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AppearanceDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/settings/AppearanceDI.kt @@ -1,25 +1,33 @@ package com.anytypeio.anytype.di.feature.settings +import androidx.lifecycle.ViewModelProvider import com.anytypeio.anytype.core_utils.di.scope.PerScreen +import com.anytypeio.anytype.di.common.ComponentDependencies import com.anytypeio.anytype.domain.config.UserSettingsRepository import com.anytypeio.anytype.domain.theme.GetTheme import com.anytypeio.anytype.domain.theme.SetTheme import com.anytypeio.anytype.ui.settings.AppearanceFragment import com.anytypeio.anytype.ui_settings.appearance.AppearanceViewModel +import dagger.Component import com.anytypeio.anytype.ui_settings.appearance.ThemeApplicator import com.anytypeio.anytype.ui_settings.appearance.ThemeApplicatorImpl +import dagger.Binds import dagger.Module import dagger.Provides -import dagger.Subcomponent -@Subcomponent(modules = [AppearanceModule::class]) +@Component( + dependencies = [AppearanceDependencies::class], + modules = [ + AppearanceModule::class, + AppearanceModule.Declarations::class + ] +) @PerScreen -interface AppearanceSubComponent { +interface AppearanceComponent { - @Subcomponent.Builder - interface Builder { - fun module(module: AppearanceModule): Builder - fun build(): AppearanceSubComponent + @Component.Factory + interface Factory { + fun create(dependencies: AppearanceDependencies): AppearanceComponent } fun inject(fragment: AppearanceFragment) @@ -27,23 +35,6 @@ interface AppearanceSubComponent { @Module object AppearanceModule { - @JvmStatic - @Provides - @PerScreen - fun provideViewModelFactory( - getTheme: GetTheme, - setTheme: SetTheme, - themeApplicator: ThemeApplicator - ): AppearanceViewModel.Factory = AppearanceViewModel.Factory( - getTheme, - setTheme, - themeApplicator - ) - - @JvmStatic - @PerScreen - @Provides - fun provideThemeApplicator(): ThemeApplicator = ThemeApplicatorImpl() @JvmStatic @Provides @@ -54,4 +45,22 @@ object AppearanceModule { @Provides @PerScreen fun provideSetThemeUseCase(repo: UserSettingsRepository): SetTheme = SetTheme(repo) + + @Module + interface Declarations { + + @PerScreen + @Binds + fun bindViewModelFactory( + factory: AppearanceViewModel.Factory + ): ViewModelProvider.Factory + + @PerScreen + @Binds + fun bindThemeApplicator(applicator: ThemeApplicatorImpl): ThemeApplicator + } +} + +interface AppearanceDependencies : ComponentDependencies { + fun userUserSettingsRepository(): UserSettingsRepository } \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/di/main/MainComponent.kt b/app/src/main/java/com/anytypeio/anytype/di/main/MainComponent.kt index 3cfb189c66..f28a3f2f8d 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/main/MainComponent.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/main/MainComponent.kt @@ -1,20 +1,26 @@ package com.anytypeio.anytype.di.main import com.anytypeio.anytype.app.AndroidApplication +import com.anytypeio.anytype.di.common.ComponentDependencies +import com.anytypeio.anytype.di.common.ComponentDependenciesKey import com.anytypeio.anytype.di.feature.* import com.anytypeio.anytype.di.feature.auth.DeletedAccountSubcomponent import com.anytypeio.anytype.di.feature.settings.AboutAppSubComponent import com.anytypeio.anytype.di.feature.settings.AccountAndDataSubComponent -import com.anytypeio.anytype.di.feature.settings.AppearanceSubComponent +import com.anytypeio.anytype.di.feature.settings.AppearanceDependencies import com.anytypeio.anytype.di.feature.settings.LogoutWarningSubComponent import com.anytypeio.anytype.di.feature.settings.MainSettingsSubComponent import com.anytypeio.anytype.di.feature.wallpaper.WallpaperSelectSubComponent +import dagger.Binds import dagger.Component +import dagger.Module +import dagger.multibindings.IntoMap import javax.inject.Singleton @Singleton @Component( modules = [ + ComponentDependenciesModule::class, ContextModule::class, DataModule::class, EventModule::class, @@ -27,7 +33,7 @@ import javax.inject.Singleton LocalNetworkAddressModule::class ] ) -interface MainComponent { +interface MainComponent : AppearanceDependencies { fun inject(app: AndroidApplication) fun splashComponentBuilder(): SplashSubComponent.Builder @@ -60,7 +66,6 @@ interface MainComponent { fun aboutAppComponent() : AboutAppSubComponent.Builder fun accountAndDataComponent() : AccountAndDataSubComponent.Builder - fun appearanceComponent() : AppearanceSubComponent.Builder fun debugSettingsBuilder(): DebugSettingsSubComponent.Builder fun keychainPhraseComponentBuilder(): KeychainPhraseSubComponent.Builder fun otherSettingsComponentBuilder(): OtherSettingsSubComponent.Builder @@ -68,4 +73,13 @@ interface MainComponent { fun mainSettingsComponent() : MainSettingsSubComponent.Builder //endregion +} + +@Module +private abstract class ComponentDependenciesModule private constructor() { + + @Binds + @IntoMap + @ComponentDependenciesKey(AppearanceDependencies::class) + abstract fun provideAppearanceDependencies (component: MainComponent): ComponentDependencies } \ No newline at end of file diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/theme/GetTheme.kt b/domain/src/main/java/com/anytypeio/anytype/domain/theme/GetTheme.kt index d5941d9825..727d03aa15 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/theme/GetTheme.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/theme/GetTheme.kt @@ -2,7 +2,6 @@ package com.anytypeio.anytype.domain.theme import com.anytypeio.anytype.core_models.ThemeMode import com.anytypeio.anytype.domain.base.BaseUseCase -import com.anytypeio.anytype.domain.base.Either import com.anytypeio.anytype.domain.config.UserSettingsRepository class GetTheme( diff --git a/ui-settings/build.gradle b/ui-settings/build.gradle index 12cc5cf09b..5a8a2de877 100644 --- a/ui-settings/build.gradle +++ b/ui-settings/build.gradle @@ -37,6 +37,8 @@ dependencies { def applicationDependencies = rootProject.ext.mainApplication def unitTestDependencies = rootProject.ext.unitTesting + compileOnly applicationDependencies.javaxInject + implementation applicationDependencies.lifecycleViewModel implementation applicationDependencies.lifecycleRuntime diff --git a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/appearance/AppearanceViewModel.kt b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/appearance/AppearanceViewModel.kt index 60b949f5fe..2afa3ecd64 100644 --- a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/appearance/AppearanceViewModel.kt +++ b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/appearance/AppearanceViewModel.kt @@ -10,6 +10,7 @@ import com.anytypeio.anytype.domain.theme.SetTheme import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import timber.log.Timber +import javax.inject.Inject class AppearanceViewModel( private val getTheme: GetTheme, @@ -65,7 +66,7 @@ class AppearanceViewModel( selectedTheme.value = themeMode } - class Factory( + class Factory @Inject constructor( private val getTheme: GetTheme, private val setTheme: SetTheme, private val themeApplicator: ThemeApplicator, diff --git a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/appearance/ThemeApplicator.kt b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/appearance/ThemeApplicator.kt index a250ce0e10..0d247a4928 100644 --- a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/appearance/ThemeApplicator.kt +++ b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/appearance/ThemeApplicator.kt @@ -2,6 +2,7 @@ package com.anytypeio.anytype.ui_settings.appearance import androidx.appcompat.app.AppCompatDelegate import com.anytypeio.anytype.core_models.ThemeMode +import javax.inject.Inject interface ThemeApplicator { @@ -9,7 +10,7 @@ interface ThemeApplicator { } -class ThemeApplicatorImpl: ThemeApplicator { +class ThemeApplicatorImpl @Inject constructor(): ThemeApplicator { override fun apply(theme: ThemeMode) { when(theme) { ThemeMode.Light -> apply(AppCompatDelegate.MODE_NIGHT_NO)