From 650a191fa795167aa29fe29eca87bf38a115b99d Mon Sep 17 00:00:00 2001 From: m_kolobanova Date: Mon, 23 Mar 2026 20:31:26 +0300 Subject: [PATCH 1/3] =?UTF-8?q?Task=201.=20=D0=9F=D1=80=D0=B8=D0=BB=D0=BE?= =?UTF-8?q?=D0=B6=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B2?= =?UTF-8?q?=D0=B5=D0=B4=D0=B5=D0=BD=D0=BE=20=D1=81=20=D0=BF=D0=B0=D1=82?= =?UTF-8?q?=D1=82=D0=B5=D1=80=D0=BD=D0=B0=20Service=20Locator=20=D0=BD?= =?UTF-8?q?=D0=B0=20DI,=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D1=83?= =?UTF-8?q?=D1=8F=20Dagger.=20=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=D0=B0?= =?UTF-8?q?=20AppComponent=20=D0=BA=D0=B0=D0=BA=20@Singleton=20=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=B4=D0=BA=D0=BB=D1=8E=D1=87=D0=B8=D0=BB=D0=B0=20?= =?UTF-8?q?=D0=B5=D0=B3=D0=BE=20=D0=BA=20Application.=20=D0=A1=D0=BE=D0=B7?= =?UTF-8?q?=D0=B4=D0=B0=D0=BB=D0=B0=20Dagger-=D0=BC=D0=BE=D0=B4=D1=83?= =?UTF-8?q?=D0=BB=D0=B8=20=D0=B2=20AppModule.kt=20=D0=B8=20SubcomponentsMo?= =?UTF-8?q?dule.kt=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=D0=B0=20?= =?UTF-8?q?=D0=B1=D0=B0=D0=B7=D0=BE=D0=B2=D1=8B=D0=B5=20DI-=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9=D1=81=D1=8B=20=D0=B2=20Dep?= =?UTF-8?q?endencies.kt=20=D0=B8=20ProductsDependencies.kt=20=D0=9E=D0=B1?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D0=B8=D0=BB=D0=B0=20ServiceLocator=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B8=20=D0=B8=D0=B7=20AppComponent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../otus/dihomework/ProductsApplication.kt | 14 ++- .../com/otus/dihomework/ServiceLocator.kt | 55 ++--------- .../com/otus/dihomework/di/AppComponent.kt | 36 +++++++ .../java/com/otus/dihomework/di/AppModule.kt | 93 +++++++++++++++++++ .../com/otus/dihomework/di/Dependencies.kt | 13 +++ .../otus/dihomework/di/FavoritesComponent.kt | 16 ++++ .../dihomework/di/FavoritesFeatureModule.kt | 13 +++ .../dihomework/di/ProductsDependencies.kt | 11 +++ .../otus/dihomework/di/SubcomponentsModule.kt | 6 ++ 9 files changed, 211 insertions(+), 46 deletions(-) create mode 100644 app/src/main/java/com/otus/dihomework/di/AppComponent.kt create mode 100644 app/src/main/java/com/otus/dihomework/di/AppModule.kt create mode 100644 app/src/main/java/com/otus/dihomework/di/Dependencies.kt create mode 100644 app/src/main/java/com/otus/dihomework/di/FavoritesComponent.kt create mode 100644 app/src/main/java/com/otus/dihomework/di/FavoritesFeatureModule.kt create mode 100644 app/src/main/java/com/otus/dihomework/di/ProductsDependencies.kt create mode 100644 app/src/main/java/com/otus/dihomework/di/SubcomponentsModule.kt diff --git a/app/src/main/java/com/otus/dihomework/ProductsApplication.kt b/app/src/main/java/com/otus/dihomework/ProductsApplication.kt index 2e3799d..4b44c95 100644 --- a/app/src/main/java/com/otus/dihomework/ProductsApplication.kt +++ b/app/src/main/java/com/otus/dihomework/ProductsApplication.kt @@ -1,10 +1,22 @@ package com.otus.dihomework import android.app.Application +import com.otus.dihomework.di.AppComponent +import com.otus.dihomework.di.DaggerAppComponent +import com.otus.dihomework.di.Dependencies +import com.otus.dihomework.di.DependenciesProvider + +class ProductsApplication : Application(), DependenciesProvider { + lateinit var appComponent: AppComponent + private set -class ProductsApplication : Application() { override fun onCreate() { super.onCreate() + appComponent = DaggerAppComponent.factory().create(this) ServiceLocator.init(this) } + + override fun getDependencies(): Dependencies { + return appComponent + } } diff --git a/app/src/main/java/com/otus/dihomework/ServiceLocator.kt b/app/src/main/java/com/otus/dihomework/ServiceLocator.kt index 1a56c01..69aad3c 100644 --- a/app/src/main/java/com/otus/dihomework/ServiceLocator.kt +++ b/app/src/main/java/com/otus/dihomework/ServiceLocator.kt @@ -1,28 +1,17 @@ package com.otus.dihomework import android.content.Context -import com.google.gson.GsonBuilder -import com.otus.dihomework.common.data.FavoritesRepositoryImpl import com.otus.dihomework.common.data.ProductApiService import com.otus.dihomework.common.data.ProductDomainMapper import com.otus.dihomework.common.data.ProductRemoteDataSource -import com.otus.dihomework.common.data.ProductRepositoryImpl import com.otus.dihomework.common.domain_api.ConsumeFavoritesUseCase import com.otus.dihomework.common.domain_api.ConsumeProductsUseCase import com.otus.dihomework.common.domain_api.ToggleFavoriteUseCase -import com.otus.dihomework.common.domain_impl.ConsumeFavoritesUseCaseImpl -import com.otus.dihomework.common.domain_impl.ConsumeProductsUseCaseImpl import com.otus.dihomework.common.domain_impl.FavoritesRepository import com.otus.dihomework.common.domain_impl.ProductRepository -import com.otus.dihomework.common.domain_impl.ToggleFavoriteUseCaseImpl import com.otus.dihomework.common.util.PriceFormatter import com.otus.dihomework.features.favorites.FavoritesStateFactory import com.otus.dihomework.features.products.ProductsStateFactory -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory -import java.util.concurrent.TimeUnit object ServiceLocator { @@ -32,34 +21,12 @@ object ServiceLocator { applicationContext = context.applicationContext } - private val httpLoggingInterceptor: HttpLoggingInterceptor by lazy { - HttpLoggingInterceptor().apply { - level = HttpLoggingInterceptor.Level.BODY - } - } - - private val okHttpClient: OkHttpClient by lazy { - OkHttpClient.Builder() - .addInterceptor(httpLoggingInterceptor) - .connectTimeout(30, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .build() - } - - private val retrofit: Retrofit by lazy { - Retrofit.Builder() - .baseUrl("https://otus-android.github.io/") - .client(okHttpClient) - .addConverterFactory(GsonConverterFactory.create(GsonBuilder().create())) - .build() - } - - private val _productApiService: ProductApiService by lazy { - retrofit.create(ProductApiService::class.java) - } + private val appComponent get() = + (checkNotNull(applicationContext) { "ServiceLocator not initialized! Call init() first." } + as ProductsApplication).appComponent fun getProductApiService(): ProductApiService { - return _productApiService + return appComponent.productApiService() } fun getProductDomainMapper(): ProductDomainMapper { @@ -71,29 +38,27 @@ object ServiceLocator { } fun getFavoritesRepository(): FavoritesRepository { - val context = applicationContext - checkNotNull(context) { "ServiceLocator not initialized! Call init() first." } - return FavoritesRepositoryImpl(context) + return appComponent.favoritesRepository() } fun getProductRepository(): ProductRepository { - return ProductRepositoryImpl() + return appComponent.productRepository() } fun getConsumeProductsUseCase(): ConsumeProductsUseCase { - return ConsumeProductsUseCaseImpl() + return appComponent.consumeProductsUseCase() } fun getConsumeFavoritesUseCase(): ConsumeFavoritesUseCase { - return ConsumeFavoritesUseCaseImpl() + return appComponent.consumeFavoritesUseCase() } fun getToggleFavoriteUseCase(): ToggleFavoriteUseCase { - return ToggleFavoriteUseCaseImpl() + return appComponent.toggleFavoriteUseCase() } fun getPriceFormatter(): PriceFormatter { - return PriceFormatter() + return appComponent.priceFormatter() } fun getProductsStateFactory(): ProductsStateFactory { diff --git a/app/src/main/java/com/otus/dihomework/di/AppComponent.kt b/app/src/main/java/com/otus/dihomework/di/AppComponent.kt new file mode 100644 index 0000000..0fbc34f --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/di/AppComponent.kt @@ -0,0 +1,36 @@ +package com.otus.dihomework.di + +import android.content.Context +import com.otus.dihomework.common.data.ProductApiService +import com.otus.dihomework.common.domain_api.ConsumeFavoritesUseCase +import com.otus.dihomework.common.domain_api.ConsumeProductsUseCase +import com.otus.dihomework.common.domain_api.ToggleFavoriteUseCase +import com.otus.dihomework.common.domain_impl.FavoritesRepository +import com.otus.dihomework.common.domain_impl.ProductRepository +import com.otus.dihomework.common.util.PriceFormatter +import dagger.BindsInstance +import dagger.Component +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import javax.inject.Singleton + +@Singleton +@Component(modules = [AppModule::class]) +interface AppComponent : ProductsDependencies { + fun context(): Context + fun okHttpClient(): OkHttpClient + fun retrofit(): Retrofit + fun productApiService(): ProductApiService + fun productRepository(): ProductRepository + fun favoritesRepository(): FavoritesRepository + override fun consumeProductsUseCase(): ConsumeProductsUseCase + fun consumeFavoritesUseCase(): ConsumeFavoritesUseCase + override fun toggleFavoriteUseCase(): ToggleFavoriteUseCase + override fun priceFormatter(): PriceFormatter + fun favoritesComponent(): FavoritesComponent.Factory + + @Component.Factory + interface Factory { + fun create(@BindsInstance context: Context): AppComponent + } +} diff --git a/app/src/main/java/com/otus/dihomework/di/AppModule.kt b/app/src/main/java/com/otus/dihomework/di/AppModule.kt new file mode 100644 index 0000000..1ebeee6 --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/di/AppModule.kt @@ -0,0 +1,93 @@ +package com.otus.dihomework.di + +import android.content.Context +import com.google.gson.GsonBuilder +import com.otus.dihomework.common.data.FavoritesRepositoryImpl +import com.otus.dihomework.common.data.ProductApiService +import com.otus.dihomework.common.data.ProductRepositoryImpl +import com.otus.dihomework.common.domain_api.ConsumeFavoritesUseCase +import com.otus.dihomework.common.domain_api.ConsumeProductsUseCase +import com.otus.dihomework.common.domain_api.ToggleFavoriteUseCase +import com.otus.dihomework.common.domain_impl.ConsumeFavoritesUseCaseImpl +import com.otus.dihomework.common.domain_impl.ConsumeProductsUseCaseImpl +import com.otus.dihomework.common.domain_impl.FavoritesRepository +import com.otus.dihomework.common.domain_impl.ProductRepository +import com.otus.dihomework.common.domain_impl.ToggleFavoriteUseCaseImpl +import com.otus.dihomework.common.util.PriceFormatter +import dagger.Module +import dagger.Provides +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit +import javax.inject.Singleton + +@Module(includes = [SubcomponentsModule::class]) +object AppModule { + @Provides + @Singleton + fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor { + return HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + } + + @Provides + @Singleton + fun provideOkHttpClient( + httpLoggingInterceptor: HttpLoggingInterceptor + ): OkHttpClient { + return OkHttpClient.Builder() + .addInterceptor(httpLoggingInterceptor) + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .build() + } + + @Provides + @Singleton + fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { + return Retrofit.Builder() + .baseUrl("https://otus-android.github.io/") + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create(GsonBuilder().create())) + .build() + } + + @Provides + @Singleton + fun provideProductApiService(retrofit: Retrofit): ProductApiService { + return retrofit.create(ProductApiService::class.java) + } + + @Provides + fun provideProductRepository(): ProductRepository { + return ProductRepositoryImpl() + } + + @Provides + fun provideFavoritesRepository(context: Context): FavoritesRepository { + return FavoritesRepositoryImpl(context) + } + + @Provides + fun provideConsumeProductsUseCase(): ConsumeProductsUseCase { + return ConsumeProductsUseCaseImpl() + } + + @Provides + fun provideConsumeFavoritesUseCase(): ConsumeFavoritesUseCase { + return ConsumeFavoritesUseCaseImpl() + } + + @Provides + fun provideToggleFavoriteUseCase(): ToggleFavoriteUseCase { + return ToggleFavoriteUseCaseImpl() + } + + @Provides + fun providePriceFormatter(): PriceFormatter { + return PriceFormatter() + } +} diff --git a/app/src/main/java/com/otus/dihomework/di/Dependencies.kt b/app/src/main/java/com/otus/dihomework/di/Dependencies.kt new file mode 100644 index 0000000..3aab20b --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/di/Dependencies.kt @@ -0,0 +1,13 @@ +package com.otus.dihomework.di + +import android.content.Context + +interface Dependencies + +interface DependenciesProvider { + fun getDependencies(): Dependencies +} + +inline fun Context.findDependencies(): T { + return (applicationContext as DependenciesProvider).getDependencies() as T +} diff --git a/app/src/main/java/com/otus/dihomework/di/FavoritesComponent.kt b/app/src/main/java/com/otus/dihomework/di/FavoritesComponent.kt new file mode 100644 index 0000000..a64ed99 --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/di/FavoritesComponent.kt @@ -0,0 +1,16 @@ +package com.otus.dihomework.di + +import com.otus.dihomework.common.di.FeatureScope +import com.otus.dihomework.features.favorites.FavoritesViewModelFactory +import dagger.Subcomponent + +@FeatureScope +@Subcomponent(modules = [FavoritesFeatureModule::class]) +interface FavoritesComponent { + fun viewModelFactory(): FavoritesViewModelFactory + + @Subcomponent.Factory + interface Factory { + fun create(): FavoritesComponent + } +} diff --git a/app/src/main/java/com/otus/dihomework/di/FavoritesFeatureModule.kt b/app/src/main/java/com/otus/dihomework/di/FavoritesFeatureModule.kt new file mode 100644 index 0000000..0299c6c --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/di/FavoritesFeatureModule.kt @@ -0,0 +1,13 @@ +package com.otus.dihomework.di + +import com.otus.dihomework.features.favorites.FavoritesViewModelFactory +import dagger.Module +import dagger.Provides + +@Module +object FavoritesFeatureModule { + @Provides + fun provideFavoritesViewModelFactory(): FavoritesViewModelFactory { + return FavoritesViewModelFactory() + } +} diff --git a/app/src/main/java/com/otus/dihomework/di/ProductsDependencies.kt b/app/src/main/java/com/otus/dihomework/di/ProductsDependencies.kt new file mode 100644 index 0000000..e4725a9 --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/di/ProductsDependencies.kt @@ -0,0 +1,11 @@ +package com.otus.dihomework.di + +import com.otus.dihomework.common.domain_api.ConsumeProductsUseCase +import com.otus.dihomework.common.domain_api.ToggleFavoriteUseCase +import com.otus.dihomework.common.util.PriceFormatter + +interface ProductsDependencies : Dependencies { + fun consumeProductsUseCase(): ConsumeProductsUseCase + fun toggleFavoriteUseCase(): ToggleFavoriteUseCase + fun priceFormatter(): PriceFormatter +} diff --git a/app/src/main/java/com/otus/dihomework/di/SubcomponentsModule.kt b/app/src/main/java/com/otus/dihomework/di/SubcomponentsModule.kt new file mode 100644 index 0000000..7c043e4 --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/di/SubcomponentsModule.kt @@ -0,0 +1,6 @@ +package com.otus.dihomework.di + +import dagger.Module + +@Module(subcomponents = [FavoritesComponent::class]) +object SubcomponentsModule From e6d1a9a3258091121622cb79abdb5192dcd7a419 Mon Sep 17 00:00:00 2001 From: m_kolobanova Date: Mon, 23 Mar 2026 22:39:25 +0300 Subject: [PATCH 2/3] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=D0=B0?= =?UTF-8?q?=20FavoritesComponent=20=D0=BA=D0=B0=D0=BA=20Subcomponent=20?= =?UTF-8?q?=D0=B8=20=D0=BF=D0=BE=D0=B4=D0=BA=D0=BB=D1=8E=D1=87=D0=B8=D0=BB?= =?UTF-8?q?=D0=B0=20=D0=B5=D0=B3=D0=BE=20=D0=BA=20=D1=8D=D0=BA=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D1=83=20=D0=B8=D0=B7=D0=B1=D1=80=D0=B0=D0=BD=D0=BD=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE.=20=D0=A3=D0=BF=D1=80=D0=BE=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D0=BB=D0=B0=20=D0=B2=D1=8B=D0=B4=D0=B0=D1=87=D1=83=20=D1=84?= =?UTF-8?q?=D0=B0=D0=B1=D1=80=D0=B8=D0=BA=D0=B8=20=D0=B2=20FavoritesViewMo?= =?UTF-8?q?delFactory.kt.=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=D0=B0=20@Inject=20constructor()=20=D0=A3=D0=B4=D0=B0=D0=BB?= =?UTF-8?q?=D0=B8=D0=BB=D0=B0=20=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D0=B9=20?= =?UTF-8?q?=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20FavoritesFeatureModule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/otus/dihomework/di/FavoritesComponent.kt | 2 +- .../otus/dihomework/di/FavoritesFeatureModule.kt | 13 ------------- .../favorites/FavoritesViewModelFactory.kt | 3 ++- .../features/favorites/ui/FavoritesScreen.kt | 15 +++++++++++++-- 4 files changed, 16 insertions(+), 17 deletions(-) delete mode 100644 app/src/main/java/com/otus/dihomework/di/FavoritesFeatureModule.kt diff --git a/app/src/main/java/com/otus/dihomework/di/FavoritesComponent.kt b/app/src/main/java/com/otus/dihomework/di/FavoritesComponent.kt index a64ed99..3a7b0b6 100644 --- a/app/src/main/java/com/otus/dihomework/di/FavoritesComponent.kt +++ b/app/src/main/java/com/otus/dihomework/di/FavoritesComponent.kt @@ -5,7 +5,7 @@ import com.otus.dihomework.features.favorites.FavoritesViewModelFactory import dagger.Subcomponent @FeatureScope -@Subcomponent(modules = [FavoritesFeatureModule::class]) +@Subcomponent interface FavoritesComponent { fun viewModelFactory(): FavoritesViewModelFactory diff --git a/app/src/main/java/com/otus/dihomework/di/FavoritesFeatureModule.kt b/app/src/main/java/com/otus/dihomework/di/FavoritesFeatureModule.kt deleted file mode 100644 index 0299c6c..0000000 --- a/app/src/main/java/com/otus/dihomework/di/FavoritesFeatureModule.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.otus.dihomework.di - -import com.otus.dihomework.features.favorites.FavoritesViewModelFactory -import dagger.Module -import dagger.Provides - -@Module -object FavoritesFeatureModule { - @Provides - fun provideFavoritesViewModelFactory(): FavoritesViewModelFactory { - return FavoritesViewModelFactory() - } -} diff --git a/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesViewModelFactory.kt b/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesViewModelFactory.kt index a30f8fc..5827bb2 100644 --- a/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesViewModelFactory.kt +++ b/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesViewModelFactory.kt @@ -2,8 +2,9 @@ package com.otus.dihomework.features.favorites import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import javax.inject.Inject -class FavoritesViewModelFactory() : ViewModelProvider.Factory { +class FavoritesViewModelFactory @Inject constructor() : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { diff --git a/app/src/main/java/com/otus/dihomework/features/favorites/ui/FavoritesScreen.kt b/app/src/main/java/com/otus/dihomework/features/favorites/ui/FavoritesScreen.kt index 6c9c79b..5e6ec76 100644 --- a/app/src/main/java/com/otus/dihomework/features/favorites/ui/FavoritesScreen.kt +++ b/app/src/main/java/com/otus/dihomework/features/favorites/ui/FavoritesScreen.kt @@ -12,22 +12,33 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import com.otus.dihomework.ProductsApplication import com.otus.dihomework.R import com.otus.dihomework.features.favorites.FavoritesScreenState import com.otus.dihomework.features.favorites.FavoritesViewModel -import com.otus.dihomework.features.favorites.FavoritesViewModelFactory @Composable fun FavoritesScreenContent( modifier: Modifier = Modifier ) { + val context = LocalContext.current + val viewModelFactory = remember(context) { + (context.applicationContext as ProductsApplication) + .appComponent + .favoritesComponent() + .create() + .viewModelFactory() + } + val viewModel: FavoritesViewModel = viewModel( - factory = FavoritesViewModelFactory() + factory = viewModelFactory ) val state by viewModel.state.collectAsState() From 791ad8fb148fcdf59a279441038728e8ccde494f Mon Sep 17 00:00:00 2001 From: m_kolobanova Date: Mon, 30 Mar 2026 22:18:34 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D1=80=D0=B5=D0=B2=D1=8C=D1=8E=20=D0=94=D0=97.=20=D0=A3=D0=B4?= =?UTF-8?q?=D0=B0=D0=BB=D0=B5=D0=BD=20ServiceLocator.kt=20=D0=B8=20=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D1=83=D0=B6=D0=BD=D1=8B=D0=B5=20=D1=84=D1=83=D0=BD?= =?UTF-8?q?=D0=BA=D1=86=D0=B8=D0=B8,=20=D0=B4=D0=BB=D1=8F=20=D0=B2=D1=81?= =?UTF-8?q?=D0=B5=D1=85=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20@Injec?= =?UTF-8?q?t=20constructor,=20=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20AppComponent.kt=20=D0=A1=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20appComponent=20=D0=B8=20view=20model=20?= =?UTF-8?q?=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=D0=B0=20=D0=B2=20com?= =?UTF-8?q?posable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/otus/dihomework/MainNavigation.kt | 22 ++++- .../otus/dihomework/ProductsApplication.kt | 1 - .../com/otus/dihomework/ServiceLocator.kt | 71 -------------- .../common/data/FavoritesRepositoryImpl.kt | 3 +- .../common/data/ProductDomainMapper.kt | 3 +- .../common/data/ProductRemoteDataSource.kt | 9 +- .../common/data/ProductRepositoryImpl.kt | 10 +- .../ConsumeFavoritesUseCaseImpl.kt | 10 +- .../domain_impl/ConsumeProductsUseCaseImpl.kt | 10 +- .../domain_impl/ToggleFavoriteUseCaseImpl.kt | 8 +- .../dihomework/common/util/PriceFormatter.kt | 4 +- .../com/otus/dihomework/di/AppComponent.kt | 20 +--- .../java/com/otus/dihomework/di/AppModule.kt | 98 ++++++++----------- .../otus/dihomework/di/ProductsComponent.kt | 16 +++ .../favorites/FavoritesStateFactory.kt | 9 +- .../features/favorites/FavoritesViewModel.kt | 14 +-- .../favorites/FavoritesViewModelFactory.kt | 6 +- .../features/favorites/ui/FavoritesScreen.kt | 14 +-- .../features/products/ProductsStateFactory.kt | 9 +- .../features/products/ProductsViewModel.kt | 14 +-- .../products/ProductsViewModelFactory.kt | 7 +- .../features/products/ui/ProductsScreen.kt | 5 +- 22 files changed, 147 insertions(+), 216 deletions(-) delete mode 100644 app/src/main/java/com/otus/dihomework/ServiceLocator.kt create mode 100644 app/src/main/java/com/otus/dihomework/di/ProductsComponent.kt diff --git a/app/src/main/java/com/otus/dihomework/MainNavigation.kt b/app/src/main/java/com/otus/dihomework/MainNavigation.kt index 6350f6e..3fed8cb 100644 --- a/app/src/main/java/com/otus/dihomework/MainNavigation.kt +++ b/app/src/main/java/com/otus/dihomework/MainNavigation.kt @@ -7,8 +7,10 @@ import androidx.compose.material.icons.filled.List import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavGraph.Companion.findStartDestination @@ -16,6 +18,9 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController +import com.otus.dihomework.di.DaggerProductsComponent +import com.otus.dihomework.di.ProductsDependencies +import com.otus.dihomework.di.findDependencies import com.otus.dihomework.features.favorites.ui.FavoritesScreenContent import com.otus.dihomework.features.products.ui.ProductsScreenContent @@ -29,6 +34,7 @@ sealed class Screen(val route: String, val titleRes: Int, val icon: ImageVector) fun MainNavigation() { val navController = rememberNavController() val screens = listOf(Screen.Products, Screen.Favorites) + val context = LocalContext.current Scaffold( topBar = { @@ -76,10 +82,22 @@ fun MainNavigation() { modifier = Modifier.padding(innerPadding) ) { composable(Screen.Products.route) { - ProductsScreenContent() + val viewModelFactory = remember(context) { + DaggerProductsComponent.factory() + .create(context.findDependencies()) + .viewModelFactory() + } + ProductsScreenContent(viewModelFactory = viewModelFactory) } composable(Screen.Favorites.route) { - FavoritesScreenContent() + val viewModelFactory = remember(context) { + (context.applicationContext as ProductsApplication) + .appComponent + .favoritesComponent() + .create() + .viewModelFactory() + } + FavoritesScreenContent(viewModelFactory = viewModelFactory) } } } diff --git a/app/src/main/java/com/otus/dihomework/ProductsApplication.kt b/app/src/main/java/com/otus/dihomework/ProductsApplication.kt index 4b44c95..aef13fe 100644 --- a/app/src/main/java/com/otus/dihomework/ProductsApplication.kt +++ b/app/src/main/java/com/otus/dihomework/ProductsApplication.kt @@ -13,7 +13,6 @@ class ProductsApplication : Application(), DependenciesProvider { override fun onCreate() { super.onCreate() appComponent = DaggerAppComponent.factory().create(this) - ServiceLocator.init(this) } override fun getDependencies(): Dependencies { diff --git a/app/src/main/java/com/otus/dihomework/ServiceLocator.kt b/app/src/main/java/com/otus/dihomework/ServiceLocator.kt deleted file mode 100644 index 69aad3c..0000000 --- a/app/src/main/java/com/otus/dihomework/ServiceLocator.kt +++ /dev/null @@ -1,71 +0,0 @@ -package com.otus.dihomework - -import android.content.Context -import com.otus.dihomework.common.data.ProductApiService -import com.otus.dihomework.common.data.ProductDomainMapper -import com.otus.dihomework.common.data.ProductRemoteDataSource -import com.otus.dihomework.common.domain_api.ConsumeFavoritesUseCase -import com.otus.dihomework.common.domain_api.ConsumeProductsUseCase -import com.otus.dihomework.common.domain_api.ToggleFavoriteUseCase -import com.otus.dihomework.common.domain_impl.FavoritesRepository -import com.otus.dihomework.common.domain_impl.ProductRepository -import com.otus.dihomework.common.util.PriceFormatter -import com.otus.dihomework.features.favorites.FavoritesStateFactory -import com.otus.dihomework.features.products.ProductsStateFactory - -object ServiceLocator { - - private var applicationContext: Context? = null - - fun init(context: Context) { - applicationContext = context.applicationContext - } - - private val appComponent get() = - (checkNotNull(applicationContext) { "ServiceLocator not initialized! Call init() first." } - as ProductsApplication).appComponent - - fun getProductApiService(): ProductApiService { - return appComponent.productApiService() - } - - fun getProductDomainMapper(): ProductDomainMapper { - return ProductDomainMapper() - } - - fun getProductRemoteDataSource(): ProductRemoteDataSource { - return ProductRemoteDataSource() - } - - fun getFavoritesRepository(): FavoritesRepository { - return appComponent.favoritesRepository() - } - - fun getProductRepository(): ProductRepository { - return appComponent.productRepository() - } - - fun getConsumeProductsUseCase(): ConsumeProductsUseCase { - return appComponent.consumeProductsUseCase() - } - - fun getConsumeFavoritesUseCase(): ConsumeFavoritesUseCase { - return appComponent.consumeFavoritesUseCase() - } - - fun getToggleFavoriteUseCase(): ToggleFavoriteUseCase { - return appComponent.toggleFavoriteUseCase() - } - - fun getPriceFormatter(): PriceFormatter { - return appComponent.priceFormatter() - } - - fun getProductsStateFactory(): ProductsStateFactory { - return ProductsStateFactory() - } - - fun getFavoritesStateFactory(): FavoritesStateFactory { - return FavoritesStateFactory() - } -} diff --git a/app/src/main/java/com/otus/dihomework/common/data/FavoritesRepositoryImpl.kt b/app/src/main/java/com/otus/dihomework/common/data/FavoritesRepositoryImpl.kt index ad70211..9e438d4 100644 --- a/app/src/main/java/com/otus/dihomework/common/data/FavoritesRepositoryImpl.kt +++ b/app/src/main/java/com/otus/dihomework/common/data/FavoritesRepositoryImpl.kt @@ -9,10 +9,11 @@ import androidx.datastore.preferences.preferencesDataStore import com.otus.dihomework.common.domain_impl.FavoritesRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import javax.inject.Inject private val Context.dataStore: DataStore by preferencesDataStore(name = "favorites") -class FavoritesRepositoryImpl( +class FavoritesRepositoryImpl @Inject constructor( private val context: Context ) : FavoritesRepository { diff --git a/app/src/main/java/com/otus/dihomework/common/data/ProductDomainMapper.kt b/app/src/main/java/com/otus/dihomework/common/data/ProductDomainMapper.kt index 762959d..0ba54f1 100644 --- a/app/src/main/java/com/otus/dihomework/common/data/ProductDomainMapper.kt +++ b/app/src/main/java/com/otus/dihomework/common/data/ProductDomainMapper.kt @@ -1,8 +1,9 @@ package com.otus.dihomework.common.data import com.otus.dihomework.common.domain_api.Product +import javax.inject.Inject -class ProductDomainMapper() { +class ProductDomainMapper @Inject constructor() { fun fromDto(dto: ProductDto): Product { return Product( diff --git a/app/src/main/java/com/otus/dihomework/common/data/ProductRemoteDataSource.kt b/app/src/main/java/com/otus/dihomework/common/data/ProductRemoteDataSource.kt index ae9935f..68d8112 100644 --- a/app/src/main/java/com/otus/dihomework/common/data/ProductRemoteDataSource.kt +++ b/app/src/main/java/com/otus/dihomework/common/data/ProductRemoteDataSource.kt @@ -1,12 +1,13 @@ package com.otus.dihomework.common.data -import com.otus.dihomework.ServiceLocator +import com.otus.dihomework.common.data.ProductApiService import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow +import javax.inject.Inject -class ProductRemoteDataSource() { - private val apiService = ServiceLocator.getProductApiService() - +class ProductRemoteDataSource @Inject constructor( + private val apiService: ProductApiService +) { fun consumeProducts(): Flow> = flow { emit(apiService.getProducts()) } diff --git a/app/src/main/java/com/otus/dihomework/common/data/ProductRepositoryImpl.kt b/app/src/main/java/com/otus/dihomework/common/data/ProductRepositoryImpl.kt index 7027dca..a8489df 100644 --- a/app/src/main/java/com/otus/dihomework/common/data/ProductRepositoryImpl.kt +++ b/app/src/main/java/com/otus/dihomework/common/data/ProductRepositoryImpl.kt @@ -1,15 +1,15 @@ package com.otus.dihomework.common.data -import com.otus.dihomework.ServiceLocator import com.otus.dihomework.common.domain_impl.ProductRepository import com.otus.dihomework.common.domain_api.Product import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import javax.inject.Inject -class ProductRepositoryImpl() : ProductRepository { - - private val remoteDataSource = ServiceLocator.getProductRemoteDataSource() - private val mapper = ServiceLocator.getProductDomainMapper() +class ProductRepositoryImpl @Inject constructor( + private val remoteDataSource: ProductRemoteDataSource, + private val mapper: ProductDomainMapper +) : ProductRepository { override fun consumeProducts(): Flow> { return remoteDataSource.consumeProducts() diff --git a/app/src/main/java/com/otus/dihomework/common/domain_impl/ConsumeFavoritesUseCaseImpl.kt b/app/src/main/java/com/otus/dihomework/common/domain_impl/ConsumeFavoritesUseCaseImpl.kt index bc9b71f..7094d2f 100644 --- a/app/src/main/java/com/otus/dihomework/common/domain_impl/ConsumeFavoritesUseCaseImpl.kt +++ b/app/src/main/java/com/otus/dihomework/common/domain_impl/ConsumeFavoritesUseCaseImpl.kt @@ -1,15 +1,15 @@ package com.otus.dihomework.common.domain_impl -import com.otus.dihomework.ServiceLocator import com.otus.dihomework.common.domain_api.ConsumeFavoritesUseCase import com.otus.dihomework.common.domain_api.ProductWithFavorite import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine +import javax.inject.Inject -class ConsumeFavoritesUseCaseImpl() : ConsumeFavoritesUseCase { - - private val productRepository = ServiceLocator.getProductRepository() - private val favoritesRepository = ServiceLocator.getFavoritesRepository() +class ConsumeFavoritesUseCaseImpl @Inject constructor( + private val productRepository: ProductRepository, + private val favoritesRepository: FavoritesRepository +) : ConsumeFavoritesUseCase { override fun invoke(): Flow> { return combine( diff --git a/app/src/main/java/com/otus/dihomework/common/domain_impl/ConsumeProductsUseCaseImpl.kt b/app/src/main/java/com/otus/dihomework/common/domain_impl/ConsumeProductsUseCaseImpl.kt index 6314f50..06e8660 100644 --- a/app/src/main/java/com/otus/dihomework/common/domain_impl/ConsumeProductsUseCaseImpl.kt +++ b/app/src/main/java/com/otus/dihomework/common/domain_impl/ConsumeProductsUseCaseImpl.kt @@ -1,16 +1,16 @@ package com.otus.dihomework.common.domain_impl -import com.otus.dihomework.ServiceLocator import com.otus.dihomework.common.domain_api.ConsumeProductsUseCase import com.otus.dihomework.common.domain_api.ProductWithFavorite import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map +import javax.inject.Inject -class ConsumeProductsUseCaseImpl() : ConsumeProductsUseCase { - - private val productRepository = ServiceLocator.getProductRepository() - private val favoritesRepository = ServiceLocator.getFavoritesRepository() +class ConsumeProductsUseCaseImpl @Inject constructor( + private val productRepository: ProductRepository, + private val favoritesRepository: FavoritesRepository +) : ConsumeProductsUseCase { override fun invoke(): Flow> { return combine( diff --git a/app/src/main/java/com/otus/dihomework/common/domain_impl/ToggleFavoriteUseCaseImpl.kt b/app/src/main/java/com/otus/dihomework/common/domain_impl/ToggleFavoriteUseCaseImpl.kt index 4b5c8b7..99b52ae 100644 --- a/app/src/main/java/com/otus/dihomework/common/domain_impl/ToggleFavoriteUseCaseImpl.kt +++ b/app/src/main/java/com/otus/dihomework/common/domain_impl/ToggleFavoriteUseCaseImpl.kt @@ -1,11 +1,11 @@ package com.otus.dihomework.common.domain_impl -import com.otus.dihomework.ServiceLocator import com.otus.dihomework.common.domain_api.ToggleFavoriteUseCase +import javax.inject.Inject -class ToggleFavoriteUseCaseImpl() : ToggleFavoriteUseCase { - - private val favoritesRepository = ServiceLocator.getFavoritesRepository() +class ToggleFavoriteUseCaseImpl @Inject constructor( + private val favoritesRepository: FavoritesRepository +) : ToggleFavoriteUseCase { override suspend fun invoke(productId: String, isFavorite: Boolean) { if (isFavorite) { diff --git a/app/src/main/java/com/otus/dihomework/common/util/PriceFormatter.kt b/app/src/main/java/com/otus/dihomework/common/util/PriceFormatter.kt index d13a12f..446678a 100644 --- a/app/src/main/java/com/otus/dihomework/common/util/PriceFormatter.kt +++ b/app/src/main/java/com/otus/dihomework/common/util/PriceFormatter.kt @@ -1,6 +1,8 @@ package com.otus.dihomework.common.util -class PriceFormatter() { +import javax.inject.Inject + +class PriceFormatter @Inject constructor() { fun format(price: Double): String { return "${price.toInt()} ₽" diff --git a/app/src/main/java/com/otus/dihomework/di/AppComponent.kt b/app/src/main/java/com/otus/dihomework/di/AppComponent.kt index 0fbc34f..b6c2b07 100644 --- a/app/src/main/java/com/otus/dihomework/di/AppComponent.kt +++ b/app/src/main/java/com/otus/dihomework/di/AppComponent.kt @@ -1,32 +1,14 @@ package com.otus.dihomework.di import android.content.Context -import com.otus.dihomework.common.data.ProductApiService -import com.otus.dihomework.common.domain_api.ConsumeFavoritesUseCase -import com.otus.dihomework.common.domain_api.ConsumeProductsUseCase -import com.otus.dihomework.common.domain_api.ToggleFavoriteUseCase -import com.otus.dihomework.common.domain_impl.FavoritesRepository -import com.otus.dihomework.common.domain_impl.ProductRepository -import com.otus.dihomework.common.util.PriceFormatter import dagger.BindsInstance import dagger.Component -import okhttp3.OkHttpClient -import retrofit2.Retrofit import javax.inject.Singleton @Singleton @Component(modules = [AppModule::class]) interface AppComponent : ProductsDependencies { - fun context(): Context - fun okHttpClient(): OkHttpClient - fun retrofit(): Retrofit - fun productApiService(): ProductApiService - fun productRepository(): ProductRepository - fun favoritesRepository(): FavoritesRepository - override fun consumeProductsUseCase(): ConsumeProductsUseCase - fun consumeFavoritesUseCase(): ConsumeFavoritesUseCase - override fun toggleFavoriteUseCase(): ToggleFavoriteUseCase - override fun priceFormatter(): PriceFormatter + fun favoritesComponent(): FavoritesComponent.Factory @Component.Factory diff --git a/app/src/main/java/com/otus/dihomework/di/AppModule.kt b/app/src/main/java/com/otus/dihomework/di/AppModule.kt index 1ebeee6..528a26c 100644 --- a/app/src/main/java/com/otus/dihomework/di/AppModule.kt +++ b/app/src/main/java/com/otus/dihomework/di/AppModule.kt @@ -1,6 +1,5 @@ package com.otus.dihomework.di -import android.content.Context import com.google.gson.GsonBuilder import com.otus.dihomework.common.data.FavoritesRepositoryImpl import com.otus.dihomework.common.data.ProductApiService @@ -13,7 +12,7 @@ import com.otus.dihomework.common.domain_impl.ConsumeProductsUseCaseImpl import com.otus.dihomework.common.domain_impl.FavoritesRepository import com.otus.dihomework.common.domain_impl.ProductRepository import com.otus.dihomework.common.domain_impl.ToggleFavoriteUseCaseImpl -import com.otus.dihomework.common.util.PriceFormatter +import dagger.Binds import dagger.Module import dagger.Provides import okhttp3.OkHttpClient @@ -24,70 +23,51 @@ import java.util.concurrent.TimeUnit import javax.inject.Singleton @Module(includes = [SubcomponentsModule::class]) -object AppModule { - @Provides - @Singleton - fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor { - return HttpLoggingInterceptor().apply { - level = HttpLoggingInterceptor.Level.BODY - } - } +interface AppModule { - @Provides - @Singleton - fun provideOkHttpClient( - httpLoggingInterceptor: HttpLoggingInterceptor - ): OkHttpClient { - return OkHttpClient.Builder() - .addInterceptor(httpLoggingInterceptor) - .connectTimeout(30, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .build() - } + @Binds + fun bindProductRepository(impl: ProductRepositoryImpl): ProductRepository - @Provides - @Singleton - fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { - return Retrofit.Builder() - .baseUrl("https://otus-android.github.io/") - .client(okHttpClient) - .addConverterFactory(GsonConverterFactory.create(GsonBuilder().create())) - .build() - } + @Binds + fun bindFavoritesRepository(impl: FavoritesRepositoryImpl): FavoritesRepository - @Provides - @Singleton - fun provideProductApiService(retrofit: Retrofit): ProductApiService { - return retrofit.create(ProductApiService::class.java) - } - - @Provides - fun provideProductRepository(): ProductRepository { - return ProductRepositoryImpl() - } + @Binds + fun bindConsumeProductsUseCase(impl: ConsumeProductsUseCaseImpl): ConsumeProductsUseCase - @Provides - fun provideFavoritesRepository(context: Context): FavoritesRepository { - return FavoritesRepositoryImpl(context) - } + @Binds + fun bindConsumeFavoritesUseCase(impl: ConsumeFavoritesUseCaseImpl): ConsumeFavoritesUseCase - @Provides - fun provideConsumeProductsUseCase(): ConsumeProductsUseCase { - return ConsumeProductsUseCaseImpl() - } + @Binds + fun bindToggleFavoriteUseCase(impl: ToggleFavoriteUseCaseImpl): ToggleFavoriteUseCase - @Provides - fun provideConsumeFavoritesUseCase(): ConsumeFavoritesUseCase { - return ConsumeFavoritesUseCaseImpl() - } + companion object { + @Provides + @Singleton + fun provideOkHttpClient(): OkHttpClient { + val loggingInterceptor = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + return OkHttpClient.Builder() + .addInterceptor(loggingInterceptor) + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .build() + } - @Provides - fun provideToggleFavoriteUseCase(): ToggleFavoriteUseCase { - return ToggleFavoriteUseCaseImpl() - } + @Provides + @Singleton + fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { + return Retrofit.Builder() + .baseUrl("https://otus-android.github.io/") + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create(GsonBuilder().create())) + .build() + } - @Provides - fun providePriceFormatter(): PriceFormatter { - return PriceFormatter() + @Provides + @Singleton + fun provideProductApiService(retrofit: Retrofit): ProductApiService { + return retrofit.create(ProductApiService::class.java) + } } } diff --git a/app/src/main/java/com/otus/dihomework/di/ProductsComponent.kt b/app/src/main/java/com/otus/dihomework/di/ProductsComponent.kt new file mode 100644 index 0000000..d8a9430 --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/di/ProductsComponent.kt @@ -0,0 +1,16 @@ +package com.otus.dihomework.di + +import com.otus.dihomework.common.di.FeatureScope +import com.otus.dihomework.features.products.ProductsViewModelFactory +import dagger.Component + +@FeatureScope +@Component(dependencies = [ProductsDependencies::class]) +interface ProductsComponent { + fun viewModelFactory(): ProductsViewModelFactory + + @Component.Factory + interface Factory { + fun create(dependencies: ProductsDependencies): ProductsComponent + } +} diff --git a/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesStateFactory.kt b/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesStateFactory.kt index d2569a4..349b4b6 100644 --- a/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesStateFactory.kt +++ b/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesStateFactory.kt @@ -1,11 +1,12 @@ package com.otus.dihomework.features.favorites -import com.otus.dihomework.ServiceLocator import com.otus.dihomework.common.domain_api.ProductWithFavorite +import com.otus.dihomework.common.util.PriceFormatter +import javax.inject.Inject -class FavoritesStateFactory() { - private val priceFormatter = ServiceLocator.getPriceFormatter() - +class FavoritesStateFactory @Inject constructor( + private val priceFormatter: PriceFormatter +) { fun create(favorites: List): List { return favorites.map { createFavoriteState(it) } } diff --git a/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesViewModel.kt b/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesViewModel.kt index 9d41c4b..0f96b04 100644 --- a/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesViewModel.kt +++ b/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesViewModel.kt @@ -2,7 +2,8 @@ package com.otus.dihomework.features.favorites import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.otus.dihomework.ServiceLocator +import com.otus.dihomework.common.domain_api.ConsumeFavoritesUseCase +import com.otus.dihomework.common.domain_api.ToggleFavoriteUseCase import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -12,12 +13,13 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import javax.inject.Inject -class FavoritesViewModel() : ViewModel() { - - private val consumeFavoritesUseCase = ServiceLocator.getConsumeFavoritesUseCase() - private val toggleFavoriteUseCase = ServiceLocator.getToggleFavoriteUseCase() - private val favoritesStateFactory = ServiceLocator.getFavoritesStateFactory() +class FavoritesViewModel @Inject constructor( + private val consumeFavoritesUseCase: ConsumeFavoritesUseCase, + private val toggleFavoriteUseCase: ToggleFavoriteUseCase, + private val favoritesStateFactory: FavoritesStateFactory +) : ViewModel() { private val _state = MutableStateFlow(FavoritesScreenState()) val state: StateFlow = _state.asStateFlow() diff --git a/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesViewModelFactory.kt b/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesViewModelFactory.kt index 5827bb2..3d02c28 100644 --- a/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesViewModelFactory.kt +++ b/app/src/main/java/com/otus/dihomework/features/favorites/FavoritesViewModelFactory.kt @@ -4,11 +4,13 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import javax.inject.Inject -class FavoritesViewModelFactory @Inject constructor() : ViewModelProvider.Factory { +class FavoritesViewModelFactory @Inject constructor( + private val viewModel: FavoritesViewModel +) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { require(modelClass == FavoritesViewModel::class.java) - return FavoritesViewModel() as T + return viewModel as T } } diff --git a/app/src/main/java/com/otus/dihomework/features/favorites/ui/FavoritesScreen.kt b/app/src/main/java/com/otus/dihomework/features/favorites/ui/FavoritesScreen.kt index 5e6ec76..c8c2ca1 100644 --- a/app/src/main/java/com/otus/dihomework/features/favorites/ui/FavoritesScreen.kt +++ b/app/src/main/java/com/otus/dihomework/features/favorites/ui/FavoritesScreen.kt @@ -12,31 +12,21 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.viewModel -import com.otus.dihomework.ProductsApplication import com.otus.dihomework.R import com.otus.dihomework.features.favorites.FavoritesScreenState import com.otus.dihomework.features.favorites.FavoritesViewModel @Composable fun FavoritesScreenContent( + viewModelFactory: ViewModelProvider.Factory, modifier: Modifier = Modifier ) { - val context = LocalContext.current - val viewModelFactory = remember(context) { - (context.applicationContext as ProductsApplication) - .appComponent - .favoritesComponent() - .create() - .viewModelFactory() - } - val viewModel: FavoritesViewModel = viewModel( factory = viewModelFactory ) diff --git a/app/src/main/java/com/otus/dihomework/features/products/ProductsStateFactory.kt b/app/src/main/java/com/otus/dihomework/features/products/ProductsStateFactory.kt index d78f6e7..2405bd5 100644 --- a/app/src/main/java/com/otus/dihomework/features/products/ProductsStateFactory.kt +++ b/app/src/main/java/com/otus/dihomework/features/products/ProductsStateFactory.kt @@ -1,11 +1,12 @@ package com.otus.dihomework.features.products -import com.otus.dihomework.ServiceLocator import com.otus.dihomework.common.domain_api.ProductWithFavorite +import com.otus.dihomework.common.util.PriceFormatter +import javax.inject.Inject -class ProductsStateFactory() { - private val priceFormatter = ServiceLocator.getPriceFormatter() - +class ProductsStateFactory @Inject constructor( + private val priceFormatter: PriceFormatter +) { fun create(products: List): List { return products .map { createProductState(it) } diff --git a/app/src/main/java/com/otus/dihomework/features/products/ProductsViewModel.kt b/app/src/main/java/com/otus/dihomework/features/products/ProductsViewModel.kt index ad2ce7d..63a1670 100644 --- a/app/src/main/java/com/otus/dihomework/features/products/ProductsViewModel.kt +++ b/app/src/main/java/com/otus/dihomework/features/products/ProductsViewModel.kt @@ -2,7 +2,8 @@ package com.otus.dihomework.features.products import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.otus.dihomework.ServiceLocator +import com.otus.dihomework.common.domain_api.ConsumeProductsUseCase +import com.otus.dihomework.common.domain_api.ToggleFavoriteUseCase import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -12,12 +13,13 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import javax.inject.Inject -class ProductsViewModel() : ViewModel() { - - private val consumeProductsUseCase = ServiceLocator.getConsumeProductsUseCase() - private val toggleFavoriteUseCase = ServiceLocator.getToggleFavoriteUseCase() - private val productsStateFactory = ServiceLocator.getProductsStateFactory() +class ProductsViewModel @Inject constructor( + private val consumeProductsUseCase: ConsumeProductsUseCase, + private val toggleFavoriteUseCase: ToggleFavoriteUseCase, + private val productsStateFactory: ProductsStateFactory +) : ViewModel() { private val _state = MutableStateFlow(ProductsScreenState()) val state: StateFlow = _state.asStateFlow() diff --git a/app/src/main/java/com/otus/dihomework/features/products/ProductsViewModelFactory.kt b/app/src/main/java/com/otus/dihomework/features/products/ProductsViewModelFactory.kt index 82defc9..12653f2 100644 --- a/app/src/main/java/com/otus/dihomework/features/products/ProductsViewModelFactory.kt +++ b/app/src/main/java/com/otus/dihomework/features/products/ProductsViewModelFactory.kt @@ -2,12 +2,15 @@ package com.otus.dihomework.features.products import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import javax.inject.Inject -class ProductsViewModelFactory() : ViewModelProvider.Factory { +class ProductsViewModelFactory @Inject constructor( + private val viewModel: ProductsViewModel +) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { require(modelClass == ProductsViewModel::class.java) - return ProductsViewModel() as T + return viewModel as T } } diff --git a/app/src/main/java/com/otus/dihomework/features/products/ui/ProductsScreen.kt b/app/src/main/java/com/otus/dihomework/features/products/ui/ProductsScreen.kt index 0072b10..c5b4a0c 100644 --- a/app/src/main/java/com/otus/dihomework/features/products/ui/ProductsScreen.kt +++ b/app/src/main/java/com/otus/dihomework/features/products/ui/ProductsScreen.kt @@ -16,17 +16,18 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.viewModel import com.otus.dihomework.features.products.ProductsScreenState import com.otus.dihomework.features.products.ProductsViewModel -import com.otus.dihomework.features.products.ProductsViewModelFactory @Composable fun ProductsScreenContent( + viewModelFactory: ViewModelProvider.Factory, modifier: Modifier = Modifier ) { val viewModel: ProductsViewModel = viewModel( - factory = ProductsViewModelFactory() + factory = viewModelFactory ) val state by viewModel.state.collectAsState()