Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

31 dugger plug in #36

Merged
merged 12 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from 13 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
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">

<application
android:name=".app.App"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/java/com/davay/android/app/App.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.davay.android.app

import android.app.Application

class App : Application() {
override fun onCreate() {
super.onCreate()
AppComponentHolder.createComponent(this)
}
}
32 changes: 32 additions & 0 deletions app/src/main/java/com/davay/android/app/AppComponent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.davay.android.app

import android.app.Application
import com.davay.android.di.ComponentHolderMode
import com.davay.android.di.DIComponent
import com.davay.android.di.DataBasedComponentHolder
import com.davay.android.network.NetworkModule
import dagger.BindsInstance
import dagger.Component
import retrofit2.Retrofit

@Component(
modules = [NetworkModule::class]
)
interface AppComponent : DIComponent {
val retrofit: Retrofit

@Component.Builder
interface Builder {

fun build(): AppComponent

@BindsInstance
fun app(app: Application): Builder
}
}

object AppComponentHolder : DataBasedComponentHolder<AppComponent, Application>() {
override val mode: ComponentHolderMode = ComponentHolderMode.GLOBAL_SINGLETON
override fun buildComponent(data: Application): AppComponent =
DaggerAppComponent.builder().app(data).build()
}
16 changes: 16 additions & 0 deletions app/src/main/java/com/davay/android/app/ViewModelFactory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.davay.android.app

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import javax.inject.Inject
import javax.inject.Provider

class ViewModelFactory @Inject constructor(
private val viewModelProviders: @JvmSuppressWildcards Map<Class<out ViewModel>, Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val provider = viewModelProviders[modelClass]
requireNotNull(provider) { "Unknown ViewModel class: ${modelClass.name}" }
return provider.get() as T
}
}
63 changes: 63 additions & 0 deletions app/src/main/java/com/davay/android/base/BaseFragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.davay.android.base

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.viewbinding.ViewBinding
import com.davay.android.di.ScreenComponent
import kotlinx.coroutines.launch

typealias Inflate<T> = (LayoutInflater, ViewGroup?, Boolean) -> T

abstract class BaseFragment<VB : ViewBinding, VM : BaseViewModel>(
private val inflate: Inflate<VB>,
) : Fragment() {

private var _binding: VB? = null
protected val binding: VB get() = _binding!!

abstract val viewModel: VM

open val viewModelFactory: ViewModelProvider.Factory by lazy {
with(diComponent()) {
viewModelFactory
}
}

protected abstract fun diComponent(): ScreenComponent

inline fun <reified VM : BaseViewModel> injectViewModel() = viewModels<VM>(
factoryProducer = { viewModelFactory }
)

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
lifecycleScope.launch {
viewModel.navigationEvents.collect { event ->
event?.let { navigate(it) }
}
}

_binding = inflate.invoke(inflater, container, false)
return binding.root
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}

protected open fun navigate(@IdRes actionId: Int) {
findNavController().navigate(actionId)
}
}
16 changes: 16 additions & 0 deletions app/src/main/java/com/davay/android/base/BaseViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.davay.android.base

import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

abstract class BaseViewModel : ViewModel() {

private val _navigationEvents = MutableStateFlow<Int?>(null)
val navigationEvents: StateFlow<Int?>
get() = _navigationEvents

fun navigate(actionId: Int) {
_navigationEvents.value = actionId
}
}
22 changes: 22 additions & 0 deletions app/src/main/java/com/davay/android/di/ComponentHolder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.davay.android.di

abstract class ComponentHolder<T : DIComponent> {

protected open val mode = ComponentHolderMode.GLOBAL_SINGLETON

private var component: T? = null

protected abstract fun buildComponent(): T
protected open fun createComponent(): T = buildComponent()

fun getComponent(): T = when (mode) {
ComponentHolderMode.GLOBAL_SINGLETON -> {
if (component == null) {
component = createComponent()
}
component!!
}

ComponentHolderMode.ALWAYS_CREATE_NEW -> createComponent()
}
}
13 changes: 13 additions & 0 deletions app/src/main/java/com/davay/android/di/ComponentHolderMode.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.davay.android.di

enum class ComponentHolderMode {
/**
* Всегда новый инстанс компонента
*/
ALWAYS_CREATE_NEW,

/**
* Глобальный синглтон, который инициализируется один раз и навсегда
*/
GLOBAL_SINGLETON
}
3 changes: 3 additions & 0 deletions app/src/main/java/com/davay/android/di/DIComponent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.davay.android.di

interface DIComponent
19 changes: 19 additions & 0 deletions app/src/main/java/com/davay/android/di/DataBasedComponentHolder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.davay.android.di

@Suppress("Detekt.LateinitUsage")
abstract class DataBasedComponentHolder<T : DIComponent, R : Any> : ComponentHolder<T>() {

private lateinit var dataForBuild: R

override fun buildComponent(): T = error("Data for build not found")
override fun createComponent(): T = buildComponent(dataForBuild)
protected abstract fun buildComponent(data: R): T

/**
* Создает компонент, который в дальнейшем можно получать, не располагая входными данными
*/
fun createComponent(data: R): T {
dataForBuild = data
return super.getComponent()
}
}
7 changes: 7 additions & 0 deletions app/src/main/java/com/davay/android/di/ScreenComponent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.davay.android.di

import com.davay.android.app.ViewModelFactory

interface ScreenComponent {
val viewModelFactory: ViewModelFactory
}
9 changes: 9 additions & 0 deletions app/src/main/java/com/davay/android/di/ViewModelKey.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.davay.android.di

import androidx.lifecycle.ViewModel
import dagger.MapKey
import kotlin.reflect.KClass

@MapKey
@Retention(AnnotationRetention.RUNTIME)
annotation class ViewModelKey(val value: KClass<out ViewModel>)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.davay.android.feature.load.di

import com.davay.android.app.AppComponent
import com.davay.android.di.ScreenComponent
import dagger.Component

@Component(
dependencies = [AppComponent::class],
modules = [LoadFragmentModule::class]
)
interface LoadFragmentComponent : ScreenComponent {

@Component.Builder
interface Builder {
fun appComponent(appComponent: AppComponent): Builder
fun build(): LoadFragmentComponent
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.davay.android.feature.load.di

import androidx.lifecycle.ViewModel
import com.davay.android.di.ViewModelKey
import com.davay.android.feature.load.presentation.LoadViewModel
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap

@Module
interface LoadFragmentModule {

@IntoMap
@ViewModelKey(LoadViewModel::class)
@Binds
fun bindVM(impl: LoadViewModel): ViewModel
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,36 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.davay.android.R
import com.davay.android.app.AppComponentHolder
import com.davay.android.base.BaseFragment
import com.davay.android.databinding.FragmentLoadBinding
import com.davay.android.di.ScreenComponent
import com.davay.android.feature.load.di.DaggerLoadFragmentComponent

class LoadFragment : BaseFragment<FragmentLoadBinding, LoadViewModel>(
FragmentLoadBinding::inflate
) {

override val viewModel: LoadViewModel by injectViewModel<LoadViewModel>()
override fun diComponent(): ScreenComponent = DaggerLoadFragmentComponent.builder()
.appComponent(AppComponentHolder.getComponent())
.build()

class LoadFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return FragmentLoadBinding.inflate(inflater, container, false).root
): View {
super.onCreateView(inflater, container, savedInstanceState)
return binding.root
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.button.setOnClickListener { _ ->
viewModel.navigate(R.id.action_loadFragment_to_mainFragment)
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.davay.android.feature.load.presentation

import com.davay.android.base.BaseViewModel
import javax.inject.Inject

class LoadViewModel @Inject constructor() : BaseViewModel()
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.davay.android.feature.main.di

import com.davay.android.app.AppComponent
import com.davay.android.di.ScreenComponent
import dagger.Component

@Component(
dependencies = [AppComponent::class],
modules = [MainFragmentModule::class]
)
interface MainFragmentComponent : ScreenComponent {

@Component.Builder
interface Builder {
fun appComponent(appComponent: AppComponent): Builder
fun build(): MainFragmentComponent
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.davay.android.feature.main.di

import androidx.lifecycle.ViewModel
import com.davay.android.di.ViewModelKey
import com.davay.android.feature.main.presentation.MainViewModel
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap

@Module
interface MainFragmentModule {

@IntoMap
@ViewModelKey(MainViewModel::class)
@Binds
fun bindVM(impl: MainViewModel): ViewModel
}
Loading
Loading