diff --git a/core/src/main/java/app/futured/arkitekt/core/BaseViewModel.kt b/core/src/main/java/app/futured/arkitekt/core/BaseViewModel.kt index 3bc05ffe..a3608921 100644 --- a/core/src/main/java/app/futured/arkitekt/core/BaseViewModel.kt +++ b/core/src/main/java/app/futured/arkitekt/core/BaseViewModel.kt @@ -33,7 +33,7 @@ abstract class BaseViewModel : ViewModel(), LifecycleObserver { "savedStateHandle not found, please check our documentation for correct savedStateHandle implementation. https://github.com/futuredapp/arkitekt/blob/4.x/README.md " ) - internal var internalSavedStateHandle: SavedStateHandle? = null + var internalSavedStateHandle: SavedStateHandle? = null set(value) { if (field == null) { field = value diff --git a/core/src/main/java/app/futured/arkitekt/core/activity/BindingViewModelActivity.kt b/core/src/main/java/app/futured/arkitekt/core/activity/BindingViewModelActivity.kt index 45b5602e..84567763 100644 --- a/core/src/main/java/app/futured/arkitekt/core/activity/BindingViewModelActivity.kt +++ b/core/src/main/java/app/futured/arkitekt/core/activity/BindingViewModelActivity.kt @@ -39,7 +39,6 @@ abstract class BindingViewModelActivity, VS : ViewState, override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - _binding = setupBindingView(this, layoutResId) { it.setVariable(brViewVariableId, this) it.setVariable(brViewModelVariableId, viewModel) diff --git a/core/src/main/java/app/futured/arkitekt/core/activity/ViewModelActivity.kt b/core/src/main/java/app/futured/arkitekt/core/activity/ViewModelActivity.kt index 2c2fc1e5..a5794278 100644 --- a/core/src/main/java/app/futured/arkitekt/core/activity/ViewModelActivity.kt +++ b/core/src/main/java/app/futured/arkitekt/core/activity/ViewModelActivity.kt @@ -1,9 +1,8 @@ package app.futured.arkitekt.core.activity +import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.ViewModelProvider import app.futured.arkitekt.core.BaseViewModel -import app.futured.arkitekt.core.ViewModelCreator import app.futured.arkitekt.core.ViewState import app.futured.arkitekt.core.event.Event import kotlin.reflect.KClass @@ -11,7 +10,7 @@ import kotlin.reflect.KClass /** * Base Activity class with built-in ViewModel support */ -abstract class ViewModelActivity, VS : ViewState> : AppCompatActivity(), ViewModelCreator { +abstract class ViewModelActivity, VS : ViewState> : AppCompatActivity() { /** * Property which holds reference to layout identifier eg. R.layout.main_activity. @@ -22,11 +21,7 @@ abstract class ViewModelActivity, VS : ViewState> : AppCo /** * Reference to Activity ViewModel */ - val viewModel: VM by lazy { - getVM().apply { - lifecycle.addObserver(this) - } - } + abstract val viewModel: VM /** * Reference to Activity ViewState @@ -36,7 +31,10 @@ abstract class ViewModelActivity, VS : ViewState> : AppCo return viewModel.viewState } - private fun getVM(): VM = ViewModelProvider(this, viewModelFactory).get(viewModelFactory.viewModelClass.java) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + lifecycle.addObserver(viewModel) + } /** * Observe event defined by event class and run observer lambda whenever event is diff --git a/core/src/main/java/app/futured/arkitekt/core/fragment/BindingViewModelFragment.kt b/core/src/main/java/app/futured/arkitekt/core/fragment/BindingViewModelFragment.kt index 9c8b2975..874d2f3d 100644 --- a/core/src/main/java/app/futured/arkitekt/core/fragment/BindingViewModelFragment.kt +++ b/core/src/main/java/app/futured/arkitekt/core/fragment/BindingViewModelFragment.kt @@ -40,6 +40,7 @@ abstract class BindingViewModelFragment, VS : ViewState, private var _binding: B? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + lifecycle.addObserver(viewModel) return setupBindingView(inflater, container, layoutResId) { it.setVariable(brViewVariableId, this) it.setVariable(brViewModelVariableId, viewModel) diff --git a/core/src/main/java/app/futured/arkitekt/core/fragment/ViewModelFragment.kt b/core/src/main/java/app/futured/arkitekt/core/fragment/ViewModelFragment.kt index 26f36532..1d14dd98 100644 --- a/core/src/main/java/app/futured/arkitekt/core/fragment/ViewModelFragment.kt +++ b/core/src/main/java/app/futured/arkitekt/core/fragment/ViewModelFragment.kt @@ -7,7 +7,6 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import app.futured.arkitekt.core.BaseViewModel -import app.futured.arkitekt.core.ViewModelCreator import app.futured.arkitekt.core.ViewState import app.futured.arkitekt.core.event.Event import kotlin.reflect.KClass @@ -15,7 +14,7 @@ import kotlin.reflect.KClass /** * Base Fragment class with built-in ViewModel support */ -abstract class ViewModelFragment, VS : ViewState> : Fragment(), ViewModelCreator { +abstract class ViewModelFragment, VS : ViewState> : Fragment() { /** * Property which holds reference to layout identifier eg. R.layout.main_fragment. @@ -26,11 +25,7 @@ abstract class ViewModelFragment, VS : ViewState> : Fragm /** * Reference to Fragment ViewModel */ - val viewModel: VM by lazy { - getVM().apply { - lifecycle.addObserver(this) - } - } + abstract val viewModel: VM /** * Reference to Fragment ViewState @@ -40,10 +35,10 @@ abstract class ViewModelFragment, VS : ViewState> : Fragm return viewModel.viewState } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = - inflater.inflate(layoutResId, container, false) - - private fun getVM(): VM = ViewModelProvider(this, viewModelFactory).get(viewModelFactory.viewModelClass.java) + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + lifecycle.addObserver(viewModel) + return inflater.inflate(layoutResId, container, false) + } /** * Get reference to Activity ViewModel. Make sure correct VM class is diff --git a/core/src/main/java/app/futured/arkitekt/core/fragment/bottomsheet/BindingViewModelBottomSheetDialogFragment.kt b/core/src/main/java/app/futured/arkitekt/core/fragment/bottomsheet/BindingViewModelBottomSheetDialogFragment.kt index 8c78f89a..b0ec67da 100644 --- a/core/src/main/java/app/futured/arkitekt/core/fragment/bottomsheet/BindingViewModelBottomSheetDialogFragment.kt +++ b/core/src/main/java/app/futured/arkitekt/core/fragment/bottomsheet/BindingViewModelBottomSheetDialogFragment.kt @@ -40,6 +40,7 @@ abstract class BindingViewModelBottomSheetDialogFragment, private var _binding: B? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + lifecycle.addObserver(viewModel) return setupBindingView(inflater, container, layoutResId) { it.setVariable(brViewVariableId, this) it.setVariable(brViewModelVariableId, viewModel) diff --git a/core/src/main/java/app/futured/arkitekt/core/fragment/bottomsheet/ViewModelBottomSheetDialogFragment.kt b/core/src/main/java/app/futured/arkitekt/core/fragment/bottomsheet/ViewModelBottomSheetDialogFragment.kt index 522f32f9..a970f5d5 100644 --- a/core/src/main/java/app/futured/arkitekt/core/fragment/bottomsheet/ViewModelBottomSheetDialogFragment.kt +++ b/core/src/main/java/app/futured/arkitekt/core/fragment/bottomsheet/ViewModelBottomSheetDialogFragment.kt @@ -7,7 +7,6 @@ import android.view.ViewGroup import androidx.fragment.app.FragmentManager import androidx.lifecycle.ViewModelProvider import app.futured.arkitekt.core.BaseViewModel -import app.futured.arkitekt.core.ViewModelCreator import app.futured.arkitekt.core.ViewState import app.futured.arkitekt.core.event.Event import com.google.android.material.bottomsheet.BottomSheetDialogFragment @@ -17,8 +16,7 @@ import kotlin.reflect.KClass * Base BottomSheetDialogFragment class with built-in ViewModel support */ abstract class ViewModelBottomSheetDialogFragment, VS : ViewState> : - BottomSheetDialogFragment(), - ViewModelCreator { + BottomSheetDialogFragment() { /** * Property which holds reference to layout identifier eg. R.layout.fragment_custom_bottomsheet. @@ -35,19 +33,15 @@ abstract class ViewModelBottomSheetDialogFragment, VS : V /** * Reference to Fragment ViewModel */ - val viewModel: VM by lazy { - getVM().apply { - lifecycle.addObserver(this) - } - } + abstract val viewModel: VM - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = - inflater.inflate(layoutResId, container, false) + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + lifecycle.addObserver(viewModel) + return inflater.inflate(layoutResId, container, false) + } fun show(fragmentManager: FragmentManager) = show(fragmentManager, fragmentTag) - private fun getVM(): VM = ViewModelProvider(this, viewModelFactory).get(viewModelFactory.viewModelClass.java) - /** * Get reference to Activity ViewModel. Make sure correct VM class is specified. */ diff --git a/core/src/main/java/app/futured/arkitekt/core/fragment/dialog/ViewModelDialogFragment.kt b/core/src/main/java/app/futured/arkitekt/core/fragment/dialog/ViewModelDialogFragment.kt index a462fa3c..f57c1469 100644 --- a/core/src/main/java/app/futured/arkitekt/core/fragment/dialog/ViewModelDialogFragment.kt +++ b/core/src/main/java/app/futured/arkitekt/core/fragment/dialog/ViewModelDialogFragment.kt @@ -8,7 +8,6 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.FragmentManager import androidx.lifecycle.ViewModelProvider import app.futured.arkitekt.core.BaseViewModel -import app.futured.arkitekt.core.ViewModelCreator import app.futured.arkitekt.core.ViewState import app.futured.arkitekt.core.event.Event import kotlin.reflect.KClass @@ -17,8 +16,7 @@ import kotlin.reflect.KClass * Base DialogFragment class with built-in ViewModel support */ abstract class ViewModelDialogFragment, VS : ViewState> : - DialogFragment(), - ViewModelCreator { + DialogFragment() { /** * Property which holds reference to layout identifier eg. R.layout.fragment_custom_bottomsheet. @@ -35,19 +33,13 @@ abstract class ViewModelDialogFragment, VS : ViewState> : /** * Reference to Fragment ViewModel */ - val viewModel: VM by lazy { - getVM().apply { - lifecycle.addObserver(this) - } - } + abstract val viewModel: VM override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = inflater.inflate(layoutResId, container, false) fun show(fragmentManager: FragmentManager) = show(fragmentManager, fragmentTag) - private fun getVM(): VM = ViewModelProvider(this, viewModelFactory).get(viewModelFactory.viewModelClass.java) - /** * Get reference to Activity ViewModel. Make sure correct VM class is specified. */ diff --git a/core/src/main/java/app/futured/arkitekt/core/ViewModelCreator.kt b/dagger/src/main/java/app/futured/arkitekt/dagger/ViewModelCreator.kt similarity index 65% rename from core/src/main/java/app/futured/arkitekt/core/ViewModelCreator.kt rename to dagger/src/main/java/app/futured/arkitekt/dagger/ViewModelCreator.kt index e8219516..1f85a0fa 100644 --- a/core/src/main/java/app/futured/arkitekt/core/ViewModelCreator.kt +++ b/dagger/src/main/java/app/futured/arkitekt/dagger/ViewModelCreator.kt @@ -1,6 +1,7 @@ -package app.futured.arkitekt.core +package app.futured.arkitekt.dagger -import app.futured.arkitekt.core.factory.ViewModelFactory +import app.futured.arkitekt.core.BaseViewModel +import app.futured.arkitekt.dagger.factory.ViewModelFactory /** * Holds reference to BaseViewModelFactory associated with particular ViewModel. diff --git a/dagger/src/main/java/app/futured/arkitekt/dagger/activity/BaseDaggerActivity.kt b/dagger/src/main/java/app/futured/arkitekt/dagger/activity/BaseDaggerActivity.kt index 5dfe1b04..fbef737a 100644 --- a/dagger/src/main/java/app/futured/arkitekt/dagger/activity/BaseDaggerActivity.kt +++ b/dagger/src/main/java/app/futured/arkitekt/dagger/activity/BaseDaggerActivity.kt @@ -1,16 +1,22 @@ package app.futured.arkitekt.dagger.activity import android.os.Bundle +import androidx.lifecycle.ViewModelProvider import app.futured.arkitekt.core.BaseViewModel import app.futured.arkitekt.core.ViewState import app.futured.arkitekt.core.activity.ViewModelActivity +import app.futured.arkitekt.dagger.ViewModelCreator import app.futured.arkitekt.dagger.inject.TestableAndroidInjection abstract class BaseDaggerActivity, VS : ViewState> : - ViewModelActivity() { + ViewModelActivity(), ViewModelCreator { + + override val viewModel: VM by lazy { getVM() } override fun onCreate(savedInstanceState: Bundle?) { TestableAndroidInjection.inject(this) super.onCreate(savedInstanceState) } + + private fun getVM(): VM = ViewModelProvider(this, viewModelFactory).get(viewModelFactory.viewModelClass.java) } diff --git a/dagger/src/main/java/app/futured/arkitekt/dagger/activity/BaseDaggerBindingActivity.kt b/dagger/src/main/java/app/futured/arkitekt/dagger/activity/BaseDaggerBindingActivity.kt index cb26cf59..a88b5d7c 100644 --- a/dagger/src/main/java/app/futured/arkitekt/dagger/activity/BaseDaggerBindingActivity.kt +++ b/dagger/src/main/java/app/futured/arkitekt/dagger/activity/BaseDaggerBindingActivity.kt @@ -2,16 +2,22 @@ package app.futured.arkitekt.dagger.activity import android.os.Bundle import androidx.databinding.ViewDataBinding +import androidx.lifecycle.ViewModelProvider import app.futured.arkitekt.core.BaseViewModel import app.futured.arkitekt.core.ViewState import app.futured.arkitekt.core.activity.BindingViewModelActivity +import app.futured.arkitekt.dagger.ViewModelCreator import app.futured.arkitekt.dagger.inject.TestableAndroidInjection abstract class BaseDaggerBindingActivity, VS : ViewState, B : ViewDataBinding> : - BindingViewModelActivity() { + BindingViewModelActivity(), ViewModelCreator { + + override val viewModel: VM by lazy { getVM() } override fun onCreate(savedInstanceState: Bundle?) { TestableAndroidInjection.inject(this) super.onCreate(savedInstanceState) } + + private fun getVM(): VM = ViewModelProvider(this, viewModelFactory).get(viewModelFactory.viewModelClass.java) } diff --git a/core/src/main/java/app/futured/arkitekt/core/factory/BaseSavedStateViewModelFactory.kt b/dagger/src/main/java/app/futured/arkitekt/dagger/factory/BaseSavedStateViewModelFactory.kt similarity index 86% rename from core/src/main/java/app/futured/arkitekt/core/factory/BaseSavedStateViewModelFactory.kt rename to dagger/src/main/java/app/futured/arkitekt/dagger/factory/BaseSavedStateViewModelFactory.kt index 443b9107..59458cd3 100644 --- a/core/src/main/java/app/futured/arkitekt/core/factory/BaseSavedStateViewModelFactory.kt +++ b/dagger/src/main/java/app/futured/arkitekt/dagger/factory/BaseSavedStateViewModelFactory.kt @@ -1,4 +1,4 @@ -package app.futured.arkitekt.core.factory +package app.futured.arkitekt.dagger.factory import androidx.lifecycle.AbstractSavedStateViewModelFactory import androidx.lifecycle.SavedStateHandle @@ -28,8 +28,8 @@ abstract class BaseSavedStateViewModelFactory >( key: String, modelClass: Class, handle: SavedStateHandle): T { - return viewModelProvider?.get().apply { + return viewModelProvider.get().apply { this.internalSavedStateHandle = handle - } as T ?: throw IllegalStateException("Unknown ViewModel class") + } as? T ?: throw IllegalStateException("Unknown ViewModel class") } } diff --git a/core/src/main/java/app/futured/arkitekt/core/factory/BaseViewModelFactory.kt b/dagger/src/main/java/app/futured/arkitekt/dagger/factory/BaseViewModelFactory.kt similarity index 96% rename from core/src/main/java/app/futured/arkitekt/core/factory/BaseViewModelFactory.kt rename to dagger/src/main/java/app/futured/arkitekt/dagger/factory/BaseViewModelFactory.kt index 5a49e9b6..f40f4037 100644 --- a/core/src/main/java/app/futured/arkitekt/core/factory/BaseViewModelFactory.kt +++ b/dagger/src/main/java/app/futured/arkitekt/dagger/factory/BaseViewModelFactory.kt @@ -1,4 +1,4 @@ -package app.futured.arkitekt.core.factory +package app.futured.arkitekt.dagger.factory import androidx.lifecycle.ViewModel import app.futured.arkitekt.core.BaseViewModel diff --git a/core/src/main/java/app/futured/arkitekt/core/factory/ViewModelFactory.kt b/dagger/src/main/java/app/futured/arkitekt/dagger/factory/ViewModelFactory.kt similarity index 84% rename from core/src/main/java/app/futured/arkitekt/core/factory/ViewModelFactory.kt rename to dagger/src/main/java/app/futured/arkitekt/dagger/factory/ViewModelFactory.kt index a6745a89..0818a920 100644 --- a/core/src/main/java/app/futured/arkitekt/core/factory/ViewModelFactory.kt +++ b/dagger/src/main/java/app/futured/arkitekt/dagger/factory/ViewModelFactory.kt @@ -1,4 +1,4 @@ -package app.futured.arkitekt.core.factory +package app.futured.arkitekt.dagger.factory import androidx.lifecycle.ViewModelProvider import app.futured.arkitekt.core.BaseViewModel diff --git a/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/BaseDaggerBindingFragment.kt b/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/BaseDaggerBindingFragment.kt index 354eb213..ff52144e 100644 --- a/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/BaseDaggerBindingFragment.kt +++ b/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/BaseDaggerBindingFragment.kt @@ -3,15 +3,19 @@ package app.futured.arkitekt.dagger.fragment import android.content.Context import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider import app.futured.arkitekt.core.BaseViewModel import app.futured.arkitekt.core.ViewState import app.futured.arkitekt.core.fragment.BindingViewModelFragment +import app.futured.arkitekt.dagger.ViewModelCreator import app.futured.arkitekt.dagger.inject.TestableAndroidInjection import dagger.android.DispatchingAndroidInjector import javax.inject.Inject abstract class BaseDaggerBindingFragment, VS : ViewState, B : ViewDataBinding> : - BindingViewModelFragment() { + BindingViewModelFragment(), ViewModelCreator { + + override val viewModel: VM by lazy { getVM() } @Inject internal lateinit var supportFragmentInjector: DispatchingAndroidInjector @@ -19,4 +23,6 @@ abstract class BaseDaggerBindingFragment, VS : ViewState, TestableAndroidInjection.inject(this) super.onAttach(context) } + + private fun getVM(): VM = ViewModelProvider(this, viewModelFactory).get(viewModelFactory.viewModelClass.java) } diff --git a/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/BaseDaggerFragment.kt b/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/BaseDaggerFragment.kt index 534c3313..325b6e61 100644 --- a/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/BaseDaggerFragment.kt +++ b/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/BaseDaggerFragment.kt @@ -1,16 +1,22 @@ package app.futured.arkitekt.dagger.fragment import android.content.Context +import androidx.lifecycle.ViewModelProvider import app.futured.arkitekt.core.BaseViewModel import app.futured.arkitekt.core.ViewState import app.futured.arkitekt.core.fragment.ViewModelFragment +import app.futured.arkitekt.dagger.ViewModelCreator import app.futured.arkitekt.dagger.inject.TestableAndroidInjection abstract class BaseDaggerFragment, VS : ViewState> : - ViewModelFragment() { + ViewModelFragment(), ViewModelCreator { + + override val viewModel: VM by lazy { getVM() } override fun onAttach(context: Context) { TestableAndroidInjection.inject(this) super.onAttach(context) } + + private fun getVM(): VM = ViewModelProvider(this, viewModelFactory).get(viewModelFactory.viewModelClass.java) } diff --git a/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/bottomsheet/BaseDaggerBindingBottomSheetDialogFragment.kt b/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/bottomsheet/BaseDaggerBindingBottomSheetDialogFragment.kt index 35c15d73..ee6cb594 100644 --- a/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/bottomsheet/BaseDaggerBindingBottomSheetDialogFragment.kt +++ b/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/bottomsheet/BaseDaggerBindingBottomSheetDialogFragment.kt @@ -2,16 +2,22 @@ package app.futured.arkitekt.dagger.fragment.bottomsheet import android.content.Context import androidx.databinding.ViewDataBinding +import androidx.lifecycle.ViewModelProvider import app.futured.arkitekt.core.BaseViewModel import app.futured.arkitekt.core.ViewState import app.futured.arkitekt.core.fragment.bottomsheet.BindingViewModelBottomSheetDialogFragment +import app.futured.arkitekt.dagger.ViewModelCreator import app.futured.arkitekt.dagger.inject.TestableAndroidInjection abstract class BaseDaggerBindingBottomSheetDialogFragment, VS : ViewState, B : ViewDataBinding> : - BindingViewModelBottomSheetDialogFragment() { + BindingViewModelBottomSheetDialogFragment(), ViewModelCreator { + + override val viewModel: VM by lazy { getVM() } override fun onAttach(context: Context) { TestableAndroidInjection.inject(this) super.onAttach(context) } + + private fun getVM(): VM = ViewModelProvider(this, viewModelFactory).get(viewModelFactory.viewModelClass.java) } diff --git a/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/bottomsheet/BaseDaggerBottomSheetDialogFragment.kt b/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/bottomsheet/BaseDaggerBottomSheetDialogFragment.kt index a1d1518f..debc6d1c 100644 --- a/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/bottomsheet/BaseDaggerBottomSheetDialogFragment.kt +++ b/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/bottomsheet/BaseDaggerBottomSheetDialogFragment.kt @@ -2,16 +2,22 @@ package app.futured.arkitekt.dagger.fragment.bottomsheet import android.content.Context import androidx.databinding.ViewDataBinding +import androidx.lifecycle.ViewModelProvider import app.futured.arkitekt.core.BaseViewModel import app.futured.arkitekt.core.ViewState import app.futured.arkitekt.core.fragment.bottomsheet.ViewModelBottomSheetDialogFragment +import app.futured.arkitekt.dagger.ViewModelCreator import app.futured.arkitekt.dagger.inject.TestableAndroidInjection abstract class BaseDaggerBottomSheetDialogFragment, VS : ViewState, B : ViewDataBinding> : - ViewModelBottomSheetDialogFragment() { + ViewModelBottomSheetDialogFragment(), ViewModelCreator { + + override val viewModel: VM by lazy { getVM() } override fun onAttach(context: Context) { TestableAndroidInjection.inject(this) super.onAttach(context) } + + private fun getVM(): VM = ViewModelProvider(this, viewModelFactory).get(viewModelFactory.viewModelClass.java) } diff --git a/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/dialog/BaseDaggerBindingDialogFragment.kt b/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/dialog/BaseDaggerBindingDialogFragment.kt index a189e0a3..b5484d2e 100644 --- a/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/dialog/BaseDaggerBindingDialogFragment.kt +++ b/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/dialog/BaseDaggerBindingDialogFragment.kt @@ -2,16 +2,22 @@ package app.futured.arkitekt.dagger.fragment.dialog import android.content.Context import androidx.databinding.ViewDataBinding +import androidx.lifecycle.ViewModelProvider import app.futured.arkitekt.core.BaseViewModel import app.futured.arkitekt.core.ViewState import app.futured.arkitekt.core.fragment.dialog.BindingViewModelDialogFragment +import app.futured.arkitekt.dagger.ViewModelCreator import app.futured.arkitekt.dagger.inject.TestableAndroidInjection abstract class BaseDaggerBindingDialogFragment, VS : ViewState, B : ViewDataBinding> : - BindingViewModelDialogFragment() { + BindingViewModelDialogFragment(), ViewModelCreator { + + override val viewModel: VM by lazy { getVM() } override fun onAttach(context: Context) { TestableAndroidInjection.inject(this) super.onAttach(context) } + + private fun getVM(): VM = ViewModelProvider(this, viewModelFactory).get(viewModelFactory.viewModelClass.java) } diff --git a/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/dialog/BaseDaggerDialogFragment.kt b/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/dialog/BaseDaggerDialogFragment.kt index 6c0064d0..6df88ebc 100644 --- a/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/dialog/BaseDaggerDialogFragment.kt +++ b/dagger/src/main/java/app/futured/arkitekt/dagger/fragment/dialog/BaseDaggerDialogFragment.kt @@ -3,20 +3,26 @@ package app.futured.arkitekt.dagger.fragment.dialog import android.content.Context import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider import app.futured.arkitekt.core.BaseViewModel import app.futured.arkitekt.core.ViewState import app.futured.arkitekt.core.fragment.dialog.ViewModelDialogFragment +import app.futured.arkitekt.dagger.ViewModelCreator import app.futured.arkitekt.dagger.inject.TestableAndroidInjection import dagger.android.DispatchingAndroidInjector import javax.inject.Inject abstract class BaseDaggerDialogFragment, VS : ViewState, B : ViewDataBinding> : - ViewModelDialogFragment() { + ViewModelDialogFragment(), ViewModelCreator { @Inject internal lateinit var supportFragmentInjector: DispatchingAndroidInjector + override val viewModel: VM by lazy { getVM() } + override fun onAttach(context: Context) { TestableAndroidInjection.inject(this) super.onAttach(context) } + + private fun getVM(): VM = ViewModelProvider(this, viewModelFactory).get(viewModelFactory.viewModelClass.java) } diff --git a/dagger/src/main/java/app/futured/arkitekt/dagger/inject/TestableAndroidInjection.kt b/dagger/src/main/java/app/futured/arkitekt/dagger/inject/TestableAndroidInjection.kt index 24b0c760..f8a53f92 100644 --- a/dagger/src/main/java/app/futured/arkitekt/dagger/inject/TestableAndroidInjection.kt +++ b/dagger/src/main/java/app/futured/arkitekt/dagger/inject/TestableAndroidInjection.kt @@ -2,7 +2,6 @@ package app.futured.arkitekt.dagger.inject import android.app.Activity import androidx.fragment.app.Fragment -import app.futured.arkitekt.core.ViewModelCreator import dagger.android.AndroidInjection import dagger.android.support.AndroidSupportInjection diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/tools/FragmentExtensions.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/tools/FragmentExtensions.kt index 7049a1f5..42c816f1 100644 --- a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/tools/FragmentExtensions.kt +++ b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/tools/FragmentExtensions.kt @@ -1,6 +1,9 @@ package app.futured.arkitekt.examplehilt.tools +import android.content.Context import android.util.Log +import android.view.View +import android.view.inputmethod.InputMethodManager import androidx.annotation.IdRes import androidx.fragment.app.Fragment import androidx.navigation.NavDirections @@ -39,3 +42,10 @@ fun Fragment.hideKeyboard() { hideKeyboard() } } + +private fun BindingViewModelFragment<*, *, *>.hideKeyboard() { + binding.root.apply { + (context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager)?.hideSoftInputFromWindow(windowToken, 0) + clearFocus() + } +} diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/tools/SavedStateHandleExtension.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/tools/SavedStateHandleExtension.kt index 320b5061..1483507f 100644 --- a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/tools/SavedStateHandleExtension.kt +++ b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/tools/SavedStateHandleExtension.kt @@ -2,5 +2,8 @@ package app.futured.arkitekt.examplehilt.tools import androidx.lifecycle.SavedStateHandle +/** + * Get mandatory value from [SavedStateHandle] or throw [IllegalArgumentException]. + */ fun SavedStateHandle.getOrThrow(key: String): T = get(key) ?: throw IllegalArgumentException("SavedStateHandle missing argument '$key'") diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/MainActivity.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/MainActivity.kt index 186b0eb8..392545ce 100644 --- a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/MainActivity.kt +++ b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/MainActivity.kt @@ -1,13 +1,13 @@ package app.futured.arkitekt.examplehilt.ui import androidx.activity.viewModels -import app.futured.arkitekt.examplehilt.ui.base.BaseHiltActivity +import app.futured.arkitekt.examplehilt.ui.base.BaseActivity import app.futured.arkitekt.sample.hilt.R import app.futured.arkitekt.sample.hilt.databinding.ActivityMainBinding import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint -class MainActivity : BaseHiltActivity(), MainView { +class MainActivity : BaseActivity(), MainView { override val layoutResId = R.layout.activity_main diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseActivity.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseActivity.kt new file mode 100644 index 00000000..ce7d6a2a --- /dev/null +++ b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseActivity.kt @@ -0,0 +1,14 @@ +package app.futured.arkitekt.examplehilt.ui.base + +import androidx.databinding.ViewDataBinding +import app.futured.arkitekt.core.BaseViewModel +import app.futured.arkitekt.core.ViewState +import app.futured.arkitekt.core.activity.BindingViewModelActivity +import app.futured.arkitekt.sample.hilt.BR + +abstract class BaseActivity, VS : ViewState, B : ViewDataBinding> : BindingViewModelActivity() { + + override val brViewVariableId = BR.view + override val brViewModelVariableId = BR.viewModel + override val brViewStateVariableId = BR.viewState +} diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseBottomSheetDialogFragment.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseBottomSheetDialogFragment.kt new file mode 100644 index 00000000..2bd05956 --- /dev/null +++ b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseBottomSheetDialogFragment.kt @@ -0,0 +1,15 @@ +package app.futured.arkitekt.examplehilt.ui.base + +import androidx.databinding.ViewDataBinding +import app.futured.arkitekt.core.BaseViewModel +import app.futured.arkitekt.core.ViewState +import app.futured.arkitekt.core.fragment.bottomsheet.BindingViewModelBottomSheetDialogFragment +import app.futured.arkitekt.sample.hilt.BR + +abstract class BaseBottomSheetDialogFragment, VS : ViewState, B : ViewDataBinding> : + BindingViewModelBottomSheetDialogFragment() { + + override val brViewModelVariableId = BR.viewModel + override val brViewStateVariableId = BR.viewState + override val brViewVariableId = BR.view +} diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseFragment.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseFragment.kt new file mode 100644 index 00000000..e85402ec --- /dev/null +++ b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseFragment.kt @@ -0,0 +1,14 @@ +package app.futured.arkitekt.examplehilt.ui.base + +import androidx.databinding.ViewDataBinding +import app.futured.arkitekt.core.BaseViewModel +import app.futured.arkitekt.core.ViewState +import app.futured.arkitekt.core.fragment.BindingViewModelFragment +import app.futured.arkitekt.sample.hilt.BR + +abstract class BaseFragment, VS : ViewState, B : ViewDataBinding> : BindingViewModelFragment() { + + override val brViewVariableId = BR.view + override val brViewModelVariableId = BR.viewModel + override val brViewStateVariableId = BR.viewState +} diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseHiltActivity.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseHiltActivity.kt deleted file mode 100644 index 7adf4e16..00000000 --- a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseHiltActivity.kt +++ /dev/null @@ -1,66 +0,0 @@ -package app.futured.arkitekt.examplehilt.ui.base - -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import androidx.databinding.DataBindingUtil -import androidx.databinding.ViewDataBinding -import androidx.fragment.app.FragmentActivity -import app.futured.arkitekt.core.BaseViewModel -import app.futured.arkitekt.core.ViewState -import app.futured.arkitekt.core.event.Event -import app.futured.arkitekt.sample.hilt.BR -import kotlin.reflect.KClass - -abstract class BaseHiltActivity, VS : ViewState, B : ViewDataBinding> : AppCompatActivity() { - - private val brViewVariableId = BR.view - private val brViewModelVariableId = BR.viewModel - private val brViewStateVariableId = BR.viewState - - /** - * Property which holds reference to layout identifier eg. R.layout.main_activity. - * You should override this in your specific Activity implementation. - */ - abstract val layoutResId: Int - - /** - * Property which holds reference to ViewModel of Activity. - * You should override this in your specific Activity implementation. - */ - abstract val viewModel: VM - - val binding: B - get() = _binding - ?: throw IllegalStateException("ViewDataBinding cannot be accessed before onCreate() method call.") - - private var _binding: B? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - lifecycle.addObserver(viewModel) - - _binding = setupBindingView(this, layoutResId) { - it.setVariable(brViewVariableId, this) - it.setVariable(brViewModelVariableId, viewModel) - it.setVariable(brViewStateVariableId, viewModel.viewState) - it.lifecycleOwner = this - } - } - - private fun setupBindingView(fragmentActivity: FragmentActivity, layoutResId: Int, set: (B) -> Unit): B { - return DataBindingUtil.setContentView(fragmentActivity, layoutResId).also { - set(it) - } - } - - /** - * Observe event defined by event class and run observer lambda whenever event is - * received. This event class must be associated with current Activity ViewState. - * @param event Observed event class - * @param observer Lambda called whenever event is received - */ - @Suppress("UNCHECKED_CAST") - fun > observeEvent(event: KClass, observer: (E) -> Unit) { - viewModel.observeEvent(this, event, observer as (Event) -> Unit) - } -} diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseHiltFragment.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseHiltFragment.kt deleted file mode 100644 index 751e1fa7..00000000 --- a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/base/BaseHiltFragment.kt +++ /dev/null @@ -1,73 +0,0 @@ -package app.futured.arkitekt.examplehilt.ui.base - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.databinding.DataBindingUtil -import androidx.databinding.ViewDataBinding -import androidx.fragment.app.Fragment -import app.futured.arkitekt.core.BaseViewModel -import app.futured.arkitekt.core.ViewState -import app.futured.arkitekt.core.event.Event -import app.futured.arkitekt.sample.hilt.BR -import kotlin.reflect.KClass - -abstract class BaseHiltFragment, VS : ViewState, B : ViewDataBinding> : Fragment() { - - private val brViewVariableId = BR.view - private val brViewModelVariableId = BR.viewModel - private val brViewStateVariableId = BR.viewState - - /** - * Property which holds reference to layout identifier eg. R.layout.main_activity. - * You should override this in your specific Activity implementation. - */ - abstract val layoutResId: Int - - /** - * Property which holds reference to ViewModel of Activity. - * You should override this in your specific Activity implementation. - */ - abstract val viewModel: VM - - val binding: B - get() = _binding - ?: throw IllegalStateException("ViewDataBinding cannot be accessed before onCreate() method call.") - - private var _binding: B? = null - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - lifecycle.addObserver(viewModel) - return setupBindingView(inflater, container, layoutResId) { - it.setVariable(brViewVariableId, this) - it.setVariable(brViewModelVariableId, viewModel) - it.setVariable(brViewStateVariableId, viewModel.viewState) - it.lifecycleOwner = this.viewLifecycleOwner - _binding = it - } - } - - private fun setupBindingView( - layoutInflater: LayoutInflater, - container: ViewGroup?, - layoutResId: Int, - set: (B) -> Unit - ): View { - val binding = DataBindingUtil.inflate(layoutInflater, layoutResId, container, false).also { - set(it) - } - return binding.root - } - - /** - * Observe event defined by event class and run observer lambda whenever event is - * received. This event class must be associated with current Activity ViewState. - * @param event Observed event class - * @param observer Lambda called whenever event is received - */ - @Suppress("UNCHECKED_CAST") - fun > observeEvent(event: KClass, observer: (E) -> Unit) { - viewModel.observeEvent(this, event, observer as (Event) -> Unit) - } -} diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeBottomSheetFragment.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeBottomSheetFragment.kt new file mode 100644 index 00000000..48634218 --- /dev/null +++ b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeBottomSheetFragment.kt @@ -0,0 +1,31 @@ +package app.futured.arkitekt.examplehilt.ui.bottomsheet + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import app.futured.arkitekt.examplehilt.ui.base.BaseBottomSheetDialogFragment +import app.futured.arkitekt.sample.hilt.R +import app.futured.arkitekt.sample.hilt.databinding.FragmentSomeBottomSheetBinding +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class SomeBottomSheetFragment : + BaseBottomSheetDialogFragment(), + SomeView { + + companion object { + fun newInstance(): SomeBottomSheetFragment = SomeBottomSheetFragment() + } + + override val viewModel: SomeViewModel by viewModels() + + override val layoutResId = R.layout.fragment_some_bottom_sheet + + override val fragmentTag = this::class.java.name + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + observeEvent(CloseEvent::class) { dismiss() } + } +} diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeEvents.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeEvents.kt new file mode 100644 index 00000000..4224dd51 --- /dev/null +++ b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeEvents.kt @@ -0,0 +1,7 @@ +package app.futured.arkitekt.examplehilt.ui.bottomsheet + +import app.futured.arkitekt.core.event.Event + +sealed class SomeEvent : Event() + +object CloseEvent : SomeEvent() diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeView.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeView.kt new file mode 100644 index 00000000..df998e06 --- /dev/null +++ b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeView.kt @@ -0,0 +1,5 @@ +package app.futured.arkitekt.examplehilt.ui.bottomsheet + +import app.futured.arkitekt.core.BaseView + +interface SomeView : BaseView diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeViewModel.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeViewModel.kt new file mode 100644 index 00000000..016415c1 --- /dev/null +++ b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeViewModel.kt @@ -0,0 +1,13 @@ +package app.futured.arkitekt.examplehilt.ui.bottomsheet + +import app.futured.arkitekt.crusecases.BaseCrViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class SomeViewModel @Inject constructor( + override val viewState: SomeViewState +) : BaseCrViewModel() { + + fun onClose() = sendEvent(CloseEvent) +} diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeViewState.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeViewState.kt new file mode 100644 index 00000000..1f2b1a78 --- /dev/null +++ b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/bottomsheet/SomeViewState.kt @@ -0,0 +1,10 @@ +package app.futured.arkitekt.examplehilt.ui.bottomsheet + +import androidx.lifecycle.SavedStateHandle +import app.futured.arkitekt.core.ViewState +import app.futured.arkitekt.examplehilt.tools.getOrThrow +import javax.inject.Inject + +class SomeViewState @Inject constructor(handle: SavedStateHandle) : ViewState { + val number = handle.getOrThrow("number") +} diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/first/FirstEvent.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/first/FirstEvent.kt index acaaa64d..dfc26335 100644 --- a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/first/FirstEvent.kt +++ b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/first/FirstEvent.kt @@ -4,3 +4,4 @@ import app.futured.arkitekt.core.event.Event sealed class FirstEvent : Event() data class NavigateToSecondFragmentEvent(val number: Int) : FirstEvent() +data class NavigateToBottomSheetEvent(val number: Int) : FirstEvent() diff --git a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/first/FirstFragment.kt b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/first/FirstFragment.kt index fbc6e2ac..20d05814 100644 --- a/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/first/FirstFragment.kt +++ b/example-hilt/src/main/java/app/futured/arkitekt/examplehilt/ui/first/FirstFragment.kt @@ -7,13 +7,13 @@ import androidx.fragment.app.viewModels import androidx.hilt.navigation.fragment.hiltNavGraphViewModels import app.futured.arkitekt.examplehilt.tools.navigateTo import app.futured.arkitekt.examplehilt.ui.NavigationViewModel -import app.futured.arkitekt.examplehilt.ui.base.BaseHiltFragment +import app.futured.arkitekt.examplehilt.ui.base.BaseFragment import app.futured.arkitekt.sample.hilt.R import app.futured.arkitekt.sample.hilt.databinding.FragmentFirstBinding import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint -class FirstFragment : BaseHiltFragment(), FirstView { +class FirstFragment : BaseFragment(), FirstView { override val viewModel: FirstViewModel by viewModels() @@ -31,5 +31,8 @@ class FirstFragment : BaseHiltFragment(), SecondView { +class SecondFragment : BaseFragment(), SecondView { override val viewModel: SecondViewModel by viewModels() diff --git a/example-hilt/src/main/res/layout/fragment_first.xml b/example-hilt/src/main/res/layout/fragment_first.xml index cc157ba7..f281aa41 100644 --- a/example-hilt/src/main/res/layout/fragment_first.xml +++ b/example-hilt/src/main/res/layout/fragment_first.xml @@ -1,6 +1,7 @@ + xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto"> @@ -22,51 +23,87 @@ - + android:clipToPadding="false" + android:paddingHorizontal="16dp"> + app:layout_constraintBottom_toTopOf="@id/random_number" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_chainStyle="packed" /> + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + app:layout_constraintBottom_toTopOf="@id/name_input_til" + app:layout_constraintTop_toBottomOf="@id/title" + tools:text="number" /> - + app:layout_constraintBottom_toTopOf="@id/text" + app:layout_constraintTop_toBottomOf="@id/random_number"> + + + + + android:layout_marginTop="8dp" + android:text="@string/name_stored_message" + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + app:layout_constraintTop_toBottomOf="@id/name_input_til" + app:layout_constraintBottom_toTopOf="@id/next_btn" /> -