diff --git a/app/src/main/java/com/otus/dihomework/ProductsApplication.kt b/app/src/main/java/com/otus/dihomework/ProductsApplication.kt index 2e3799d..f91b0d3 100644 --- a/app/src/main/java/com/otus/dihomework/ProductsApplication.kt +++ b/app/src/main/java/com/otus/dihomework/ProductsApplication.kt @@ -1,10 +1,23 @@ 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() - ServiceLocator.init(this) + appComponent = DaggerAppComponent.factory().create(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 deleted file mode 100644 index 1a56c01..0000000 --- a/app/src/main/java/com/otus/dihomework/ServiceLocator.kt +++ /dev/null @@ -1,106 +0,0 @@ -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 { - - private var applicationContext: Context? = null - - fun init(context: Context) { - 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) - } - - fun getProductApiService(): ProductApiService { - return _productApiService - } - - fun getProductDomainMapper(): ProductDomainMapper { - return ProductDomainMapper() - } - - fun getProductRemoteDataSource(): ProductRemoteDataSource { - return ProductRemoteDataSource() - } - - fun getFavoritesRepository(): FavoritesRepository { - val context = applicationContext - checkNotNull(context) { "ServiceLocator not initialized! Call init() first." } - return FavoritesRepositoryImpl(context) - } - - fun getProductRepository(): ProductRepository { - return ProductRepositoryImpl() - } - - fun getConsumeProductsUseCase(): ConsumeProductsUseCase { - return ConsumeProductsUseCaseImpl() - } - - fun getConsumeFavoritesUseCase(): ConsumeFavoritesUseCase { - return ConsumeFavoritesUseCaseImpl() - } - - fun getToggleFavoriteUseCase(): ToggleFavoriteUseCase { - return ToggleFavoriteUseCaseImpl() - } - - fun getPriceFormatter(): PriceFormatter { - return 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..79c9b35 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,13 @@ 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 +import javax.inject.Singleton private val Context.dataStore: DataStore by preferencesDataStore(name = "favorites") -class FavoritesRepositoryImpl( +@Singleton +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..9efe473 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,12 @@ package com.otus.dihomework.common.data -import com.otus.dihomework.ServiceLocator 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..22585ad 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,17 @@ 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 +import javax.inject.Singleton -class ProductRepositoryImpl() : ProductRepository { - - private val remoteDataSource = ServiceLocator.getProductRemoteDataSource() - private val mapper = ServiceLocator.getProductDomainMapper() +@Singleton +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 new file mode 100644 index 0000000..c67474e --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/di/AppComponent.kt @@ -0,0 +1,26 @@ +package com.otus.dihomework.di + +import android.content.Context +import com.otus.dihomework.features.favorites.di.FavoritesComponent +import dagger.BindsInstance +import dagger.Component +import javax.inject.Singleton + +@Singleton +@Component(modules = [ + NetworkModule::class, + BindsModule::class, + SubcomponentsModule::class +]) +interface AppComponent : ProductsDependencies { + + // Для Component Dependencies (Products) методы уже объявлены в ProductsDependencies + + // Для Subcomponent (Favorites) + fun favoritesComponent(): FavoritesComponent.Factory + + @Component.Factory + interface Factory { + fun create(@BindsInstance context: Context): AppComponent + } +} \ No newline at end of file diff --git a/app/src/main/java/com/otus/dihomework/di/BindsModule.kt b/app/src/main/java/com/otus/dihomework/di/BindsModule.kt new file mode 100644 index 0000000..48c435c --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/di/BindsModule.kt @@ -0,0 +1,35 @@ +package com.otus.dihomework.di + +import com.otus.dihomework.common.data.FavoritesRepositoryImpl +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 dagger.Binds +import dagger.Module +import javax.inject.Singleton + +@Module +interface BindsModule { + @Binds + @Singleton + fun bindFavoritesRepository(impl: FavoritesRepositoryImpl): FavoritesRepository + + @Binds + @Singleton + fun bindProductRepository(impl: ProductRepositoryImpl): ProductRepository + + @Binds + fun bindConsumeProductsUseCase(impl: ConsumeProductsUseCaseImpl): ConsumeProductsUseCase + + @Binds + fun bindConsumeFavoritesUseCase(impl: ConsumeFavoritesUseCaseImpl): ConsumeFavoritesUseCase + + @Binds + fun bindToggleFavoriteUseCase(impl: ToggleFavoriteUseCaseImpl): ToggleFavoriteUseCase +} \ No newline at end of file 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..d2014e7 --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/di/Dependencies.kt @@ -0,0 +1,22 @@ +package com.otus.dihomework.di + +import android.content.Context +import com.otus.dihomework.common.domain_api.ConsumeProductsUseCase +import com.otus.dihomework.common.domain_api.ToggleFavoriteUseCase +import com.otus.dihomework.common.util.PriceFormatter + +interface Dependencies +interface DependenciesProvider { + fun getDependencies(): Dependencies +} + +inline fun Context.findDependencies(): T { + return (applicationContext as DependenciesProvider).getDependencies() as T +} + +// Зависимости, которые требуются фиче Products +interface ProductsDependencies : Dependencies { + fun consumeProductsUseCase(): ConsumeProductsUseCase + fun toggleFavoriteUseCase(): ToggleFavoriteUseCase + fun priceFormatter(): PriceFormatter +} \ No newline at end of file diff --git a/app/src/main/java/com/otus/dihomework/di/NetworkModule.kt b/app/src/main/java/com/otus/dihomework/di/NetworkModule.kt new file mode 100644 index 0000000..b746df7 --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/di/NetworkModule.kt @@ -0,0 +1,44 @@ +package com.otus.dihomework.di + +import com.google.gson.GsonBuilder +import com.otus.dihomework.common.data.ProductApiService +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 +class NetworkModule { + @Provides + @Singleton + fun provideOkHttpClient(): OkHttpClient { + val interceptor = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + return OkHttpClient.Builder() + .addInterceptor(interceptor) + .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) + } +} \ No newline at end of file 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..7d8a7ac --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/di/SubcomponentsModule.kt @@ -0,0 +1,7 @@ +package com.otus.dihomework.di + +import com.otus.dihomework.features.favorites.di.FavoritesComponent +import dagger.Module + +@Module(subcomponents = [FavoritesComponent::class]) +class SubcomponentsModule \ No newline at end of file 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..56c6c82 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,10 +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 a30f8fc..7c6c92b 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,12 +2,16 @@ package com.otus.dihomework.features.favorites import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import javax.inject.Inject +import javax.inject.Provider -class FavoritesViewModelFactory() : ViewModelProvider.Factory { +class FavoritesViewModelFactory @Inject constructor( + private val viewModelProvider: Provider +) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { require(modelClass == FavoritesViewModel::class.java) - return FavoritesViewModel() as T + return viewModelProvider.get() as T } } diff --git a/app/src/main/java/com/otus/dihomework/features/favorites/di/FavoritesComponent.kt b/app/src/main/java/com/otus/dihomework/features/favorites/di/FavoritesComponent.kt new file mode 100644 index 0000000..dd7fb04 --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/features/favorites/di/FavoritesComponent.kt @@ -0,0 +1,17 @@ +package com.otus.dihomework.features.favorites.di + +import com.otus.dihomework.common.di.FeatureScope +import com.otus.dihomework.features.favorites.FavoritesViewModelFactory +import dagger.Subcomponent + +@FeatureScope +@Subcomponent +interface FavoritesComponent { + + fun viewModelFactory(): FavoritesViewModelFactory + + @Subcomponent.Factory + interface Factory { + fun create(): FavoritesComponent + } +} \ No newline at end of file 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..a26473e 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,11 +12,14 @@ 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.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext 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 @@ -26,8 +29,15 @@ import com.otus.dihomework.features.favorites.FavoritesViewModelFactory fun FavoritesScreenContent( modifier: Modifier = Modifier ) { + val context = LocalContext.current + + val component = remember { + val appComponent = (context.applicationContext as ProductsApplication).appComponent + appComponent.favoritesComponent().create() + } + val viewModel: FavoritesViewModel = viewModel( - factory = FavoritesViewModelFactory() + factory = component.viewModelFactory() ) val state by viewModel.state.collectAsState() 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..c240c3f 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,16 @@ package com.otus.dihomework.features.products import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import javax.inject.Inject +import javax.inject.Provider -class ProductsViewModelFactory() : ViewModelProvider.Factory { +class ProductsViewModelFactory @Inject constructor( + private val viewModelProvider: Provider +) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { require(modelClass == ProductsViewModel::class.java) - return ProductsViewModel() as T + return viewModelProvider.get() as T } } diff --git a/app/src/main/java/com/otus/dihomework/features/products/di/ProductsComponent.kt b/app/src/main/java/com/otus/dihomework/features/products/di/ProductsComponent.kt new file mode 100644 index 0000000..1f04d7e --- /dev/null +++ b/app/src/main/java/com/otus/dihomework/features/products/di/ProductsComponent.kt @@ -0,0 +1,18 @@ +package com.otus.dihomework.features.products.di + +import com.otus.dihomework.common.di.FeatureScope +import com.otus.dihomework.di.ProductsDependencies +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 + } +} \ No newline at end of file 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..843aa10 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 @@ -13,20 +13,32 @@ 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.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import com.otus.dihomework.di.ProductsDependencies +import com.otus.dihomework.di.findDependencies import com.otus.dihomework.features.products.ProductsScreenState import com.otus.dihomework.features.products.ProductsViewModel import com.otus.dihomework.features.products.ProductsViewModelFactory +import com.otus.dihomework.features.products.di.DaggerProductsComponent @Composable fun ProductsScreenContent( modifier: Modifier = Modifier ) { + val context = LocalContext.current + val dependencies = remember { context.findDependencies() } + + val component = remember { + DaggerProductsComponent.factory().create(dependencies) + } + val viewModel: ProductsViewModel = viewModel( - factory = ProductsViewModelFactory() + factory = component.viewModelFactory() ) val state by viewModel.state.collectAsState()