Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion app/src/main/java/com/otus/dihomework/ProductsApplication.kt
Original file line number Diff line number Diff line change
@@ -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
}
}
55 changes: 10 additions & 45 deletions app/src/main/java/com/otus/dihomework/ServiceLocator.kt
Original file line number Diff line number Diff line change
@@ -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 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

этот файл вообще больше не нужен, мы его заменяем через Dagger

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Удалила


Expand All @@ -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 {
Expand All @@ -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 {
Expand Down
36 changes: 36 additions & 0 deletions app/src/main/java/com/otus/dihomework/di/AppComponent.kt
Original file line number Diff line number Diff line change
@@ -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 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

из компонента наружу торчит почти все подряд, это лишнее, по заданию нужно реализовать ProductsComponent

@FeatureScope
@Component(dependencies = [ProductsDependencies::class])
interface ProductsComponent {
    fun viewModelFactory(): ProductsViewModelFactory

    @Component.Factory
    interface Factory {
        fun create(dependencies: ProductsDependencies): ProductsComponent
    }
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Удалила все ненужное. Оставила:

  1. наследование от ProductsDependencies (для передачи в ProductsComponent)
  2. Фабрику сабкомпонента FavoritesComponent

fun context(): Context
fun okHttpClient(): OkHttpClient
fun retrofit(): Retrofit
fun productApiService(): ProductApiService
fun productRepository(): ProductRepository
fun favoritesRepository(): FavoritesRepository
override fun consumeProductsUseCase(): ConsumeProductsUseCase

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

вот эти все оверрайды тут не нужны, можно удалить

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Удалила

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
}
}
93 changes: 93 additions & 0 deletions app/src/main/java/com/otus/dihomework/di/AppModule.kt
Original file line number Diff line number Diff line change
@@ -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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

указание реализаций интерфейса делается через @Binds, а не @Provides:

@Binds
fun bindProductRepository(impl: ProductRepositoryImpl): ProductRepository

все ниже аналогично

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ой. Переделала

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 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

если у класса нет интерфейса, то его нужно в граф добавлять через @Inject constructor

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Слелала.

return PriceFormatter()
}
}
13 changes: 13 additions & 0 deletions app/src/main/java/com/otus/dihomework/di/Dependencies.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.otus.dihomework.di

import android.content.Context

interface Dependencies

interface DependenciesProvider {
fun getDependencies(): Dependencies
}

inline fun <reified T : Dependencies> Context.findDependencies(): T {
return (applicationContext as DependenciesProvider).getDependencies() as T
}
16 changes: 16 additions & 0 deletions app/src/main/java/com/otus/dihomework/di/FavoritesComponent.kt
Original file line number Diff line number Diff line change
@@ -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
interface FavoritesComponent {
fun viewModelFactory(): FavoritesViewModelFactory

@Subcomponent.Factory
interface Factory {
fun create(): FavoritesComponent
}
}
11 changes: 11 additions & 0 deletions app/src/main/java/com/otus/dihomework/di/ProductsDependencies.kt
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.otus.dihomework.di

import dagger.Module

@Module(subcomponents = [FavoritesComponent::class])
object SubcomponentsModule
Original file line number Diff line number Diff line change
Expand Up @@ -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 <T : ViewModel> create(modelClass: Class<T>): T {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

создание компонента и в целом вьюмодели лучше вынести наружу, например в composable навигации

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вынесла в MainNavigation в composable

.viewModelFactory()
}

val viewModel: FavoritesViewModel = viewModel(
factory = FavoritesViewModelFactory()
factory = viewModelFactory
)

val state by viewModel.state.collectAsState()
Expand Down