From ded233de2533e838993306e41531e45a1268bfb5 Mon Sep 17 00:00:00 2001 From: kedzie Date: Fri, 14 Jun 2019 11:46:02 -0500 Subject: [PATCH] 467 add viewmodel saved state support --- .../examples/androidx-samples/build.gradle | 1 + .../components/mvvm/SavedStateViewModel.kt | 7 ++ .../org/koin/sample/androidx/di/AppModule.kt | 3 + .../koin/sample/androidx/mvvm/MVVMActivity.kt | 5 ++ koin-projects/koin-androidx-ext/build.gradle | 7 ++ .../koin-androidx-viewmodel/build.gradle | 8 +++ .../androidx/viewmodel/ViewModelParameters.kt | 2 + .../androidx/viewmodel/ViewModelResolution.kt | 31 ++++++-- .../viewmodel/ViewModelScopeResolution.kt | 10 +++ .../viewmodel/ext/android/FragmentExt.kt | 1 + .../ext/android/LifecycleOwnerExt.kt | 66 ++++++++++++++--- .../viewmodel/ext/android/ScopeExt.kt | 72 +++++++++++++++++++ 12 files changed, 198 insertions(+), 15 deletions(-) create mode 100644 koin-projects/examples/androidx-samples/src/main/java/org/koin/sample/androidx/components/mvvm/SavedStateViewModel.kt diff --git a/koin-projects/examples/androidx-samples/build.gradle b/koin-projects/examples/androidx-samples/build.gradle index 9af1e96ad..8b591c57e 100644 --- a/koin-projects/examples/androidx-samples/build.gradle +++ b/koin-projects/examples/androidx-samples/build.gradle @@ -31,6 +31,7 @@ dependencies { implementation "androidx.appcompat:appcompat:$androidx_lib_version" implementation "androidx.lifecycle:lifecycle-extensions:$android_arch_version" + implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:1.0.0-alpha01" implementation "org.jetbrains.anko:anko-commons:$anko_version" } diff --git a/koin-projects/examples/androidx-samples/src/main/java/org/koin/sample/androidx/components/mvvm/SavedStateViewModel.kt b/koin-projects/examples/androidx-samples/src/main/java/org/koin/sample/androidx/components/mvvm/SavedStateViewModel.kt new file mode 100644 index 000000000..769574492 --- /dev/null +++ b/koin-projects/examples/androidx-samples/src/main/java/org/koin/sample/androidx/components/mvvm/SavedStateViewModel.kt @@ -0,0 +1,7 @@ +package org.koin.sample.androidx.components.mvvm + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import org.koin.sample.androidx.components.main.Service + +class SavedStateViewModel(val handle: SavedStateHandle, val id: String, val service: Service) : ViewModel() \ No newline at end of file diff --git a/koin-projects/examples/androidx-samples/src/main/java/org/koin/sample/androidx/di/AppModule.kt b/koin-projects/examples/androidx-samples/src/main/java/org/koin/sample/androidx/di/AppModule.kt index edcde93ce..4c2082e97 100644 --- a/koin-projects/examples/androidx-samples/src/main/java/org/koin/sample/androidx/di/AppModule.kt +++ b/koin-projects/examples/androidx-samples/src/main/java/org/koin/sample/androidx/di/AppModule.kt @@ -1,5 +1,6 @@ package org.koin.sample.androidx.di +import androidx.lifecycle.SavedStateHandle import org.koin.androidx.experimental.dsl.viewModel import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.core.qualifier.named @@ -15,6 +16,7 @@ import org.koin.sample.androidx.components.main.ServiceImpl import org.koin.sample.androidx.components.mvp.FactoryPresenter import org.koin.sample.androidx.components.mvp.ScopedPresenter import org.koin.sample.androidx.components.mvvm.ExtSimpleViewModel +import org.koin.sample.androidx.components.mvvm.SavedStateViewModel import org.koin.sample.androidx.components.mvvm.SimpleViewModel import org.koin.sample.androidx.components.scope.Session import org.koin.sample.androidx.mvp.MVPActivity @@ -43,6 +45,7 @@ val mvvmModule = module { viewModel(named("vm1")) { (id: String) -> SimpleViewModel(id, get()) } viewModel(named("vm2")) { (id: String) -> SimpleViewModel(id, get()) } + viewModel { (handle: SavedStateHandle, id: String) -> SavedStateViewModel(handle, id, get()) } scope(named()) { scoped { Session() } diff --git a/koin-projects/examples/androidx-samples/src/main/java/org/koin/sample/androidx/mvvm/MVVMActivity.kt b/koin-projects/examples/androidx-samples/src/main/java/org/koin/sample/androidx/mvvm/MVVMActivity.kt index 8e8a7aa06..95cb6b041 100644 --- a/koin-projects/examples/androidx-samples/src/main/java/org/koin/sample/androidx/mvvm/MVVMActivity.kt +++ b/koin-projects/examples/androidx-samples/src/main/java/org/koin/sample/androidx/mvvm/MVVMActivity.kt @@ -8,11 +8,13 @@ import org.koin.android.ext.android.getKoin import org.koin.androidx.scope.currentScope import org.koin.androidx.viewmodel.ext.android.getViewModel import org.koin.androidx.viewmodel.ext.android.viewModel +import org.koin.androidx.viewmodel.ext.android.viewModelWithState import org.koin.core.parameter.parametersOf import org.koin.core.qualifier.named import org.koin.sample.android.R import org.koin.sample.androidx.components.ID import org.koin.sample.androidx.components.mvvm.ExtSimpleViewModel +import org.koin.sample.androidx.components.mvvm.SavedStateViewModel import org.koin.sample.androidx.components.mvvm.SimpleViewModel import org.koin.sample.androidx.components.scope.Session import org.koin.sample.androidx.scope.ScopedActivityA @@ -28,6 +30,9 @@ class MVVMActivity : AppCompatActivity() { val scopeVm: ExtSimpleViewModel by currentScope.viewModel(this) val extScopeVm: ExtSimpleViewModel by currentScope.viewModel(this, named("ext")) + val savedVm: SavedStateViewModel by viewModelWithState(defaultArguments = Bundle()) { parametersOf("vm2") } + val scopedSavedVm: SavedStateViewModel by currentScope.viewModelWithState(this, defaultArguments = Bundle()) { parametersOf("vm2") } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/koin-projects/koin-androidx-ext/build.gradle b/koin-projects/koin-androidx-ext/build.gradle index 04133e335..73690cca1 100644 --- a/koin-projects/koin-androidx-ext/build.gradle +++ b/koin-projects/koin-androidx-ext/build.gradle @@ -34,6 +34,13 @@ dependencies { exclude module: "runtime" exclude group: "androidx.legacy" } + implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:1.0.0-alpha01") { + exclude module: "lifecycle-livedata" + exclude module: "lifecycle-service" + exclude module: "lifecycle-process" + exclude module: "runtime" + exclude group: "androidx.legacy" + } } apply from: '../gradle/publish-android.gradle' \ No newline at end of file diff --git a/koin-projects/koin-androidx-viewmodel/build.gradle b/koin-projects/koin-androidx-viewmodel/build.gradle index 5d200d0f0..46f4e0f8a 100644 --- a/koin-projects/koin-androidx-viewmodel/build.gradle +++ b/koin-projects/koin-androidx-viewmodel/build.gradle @@ -33,6 +33,14 @@ dependencies { exclude module: "runtime" exclude group: "androidx.legacy" } + + implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:1.0.0-alpha01") { + exclude module: "lifecycle-livedata" + exclude module: "lifecycle-service" + exclude module: "lifecycle-process" + exclude module: "runtime" + exclude group: "androidx.legacy" + } } apply from: '../gradle/publish-android.gradle' \ No newline at end of file diff --git a/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ViewModelParameters.kt b/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ViewModelParameters.kt index 568f3c111..d9f41c4e0 100644 --- a/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ViewModelParameters.kt +++ b/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ViewModelParameters.kt @@ -1,5 +1,6 @@ package org.koin.androidx.viewmodel +import android.os.Bundle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModelStoreOwner import org.koin.core.parameter.ParametersDefinition @@ -8,6 +9,7 @@ import kotlin.reflect.KClass class ViewModelParameters( val clazz: KClass, + val defaultArguments: Bundle?, val owner: LifecycleOwner, val qualifier: Qualifier? = null, val from: ViewModelStoreOwnerDefinition? = null, diff --git a/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ViewModelResolution.kt b/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ViewModelResolution.kt index 92a1ddaf4..d0230bfba 100644 --- a/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ViewModelResolution.kt +++ b/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ViewModelResolution.kt @@ -2,14 +2,14 @@ package org.koin.androidx.viewmodel import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.ViewModelStore +import androidx.lifecycle.* +import androidx.savedstate.SavedStateRegistryOwner import org.koin.core.Koin import org.koin.core.KoinApplication import org.koin.core.KoinApplication.Companion.logger import org.koin.core.logger.Level +import org.koin.core.parameter.emptyParametersHolder +import org.koin.core.parameter.parametersOf import org.koin.core.scope.Scope import org.koin.core.time.measureDuration @@ -66,4 +66,27 @@ fun Scope.createViewModelProvider( return get(parameters.clazz, parameters.qualifier, parameters.parameters) } }) +} + +//saved state + +fun Koin.getViewModelWithState(parameters: ViewModelParameters): T { + val vmStore: ViewModelStore = parameters.owner.getViewModelStore(parameters) + val viewModelProvider = rootScope.createSavedStateViewModelProvider(vmStore, parameters) + return viewModelProvider.getInstance(parameters) +} + +fun Scope.createSavedStateViewModelProvider( + vmStore: ViewModelStore, + parameters: ViewModelParameters +): ViewModelProvider { + return ViewModelProvider( + vmStore, + object : AbstractSavedStateVMFactory(vmStore as SavedStateRegistryOwner, parameters.defaultArguments) { + override fun create(key: String, modelClass: Class, handle: SavedStateHandle): T { + return get(parameters.clazz, parameters.qualifier) { + parametersOf(handle, *(parameters.parameters?.invoke() ?: emptyParametersHolder()).values) + } + } + }) } \ No newline at end of file diff --git a/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ViewModelScopeResolution.kt b/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ViewModelScopeResolution.kt index db339ddb0..a568d3758 100644 --- a/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ViewModelScopeResolution.kt +++ b/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ViewModelScopeResolution.kt @@ -12,4 +12,14 @@ fun Scope.getViewModel(parameters: ViewModelParameters): T { val vmStore: ViewModelStore = parameters.owner.getViewModelStore(parameters) val viewModelProvider = createViewModelProvider(vmStore, parameters) return viewModelProvider.getInstance(parameters) +} + +/** + * resolve instance with state + * @param parameters + */ +fun Scope.getViewModelWithState(parameters: ViewModelParameters): T { + val vmStore: ViewModelStore = parameters.owner.getViewModelStore(parameters) + val viewModelProvider = createSavedStateViewModelProvider(vmStore, parameters) + return viewModelProvider.getInstance(parameters) } \ No newline at end of file diff --git a/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ext/android/FragmentExt.kt b/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ext/android/FragmentExt.kt index f39cdef6f..f4a87ab67 100644 --- a/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ext/android/FragmentExt.kt +++ b/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ext/android/FragmentExt.kt @@ -92,6 +92,7 @@ fun Fragment.getSharedViewModel( return getKoin().getViewModel( ViewModelParameters( clazz, + null, this@getSharedViewModel, qualifier, from, diff --git a/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ext/android/LifecycleOwnerExt.kt b/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ext/android/LifecycleOwnerExt.kt index 6666b8cb4..34c227a76 100644 --- a/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ext/android/LifecycleOwnerExt.kt +++ b/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ext/android/LifecycleOwnerExt.kt @@ -16,11 +16,13 @@ package org.koin.androidx.viewmodel.ext.android import android.content.ComponentCallbacks +import android.os.Bundle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel import org.koin.android.ext.android.getKoin import org.koin.androidx.viewmodel.ViewModelParameters import org.koin.androidx.viewmodel.getViewModel +import org.koin.androidx.viewmodel.getViewModelWithState import org.koin.core.parameter.ParametersDefinition import org.koin.core.qualifier.Qualifier import kotlin.reflect.KClass @@ -44,17 +46,6 @@ fun LifecycleOwner.viewModel( parameters: ParametersDefinition? = null ): Lazy = lazy { getViewModel(clazz, qualifier, parameters) } -/** - * Lazy getByClass a viewModel instance - * - * @param qualifier - Koin BeanDefinition qualifier (if have several ViewModel beanDefinition of the same type) - * @param parameters - parameters to pass to the BeanDefinition - */ -inline fun LifecycleOwner.viewModel( - qualifier: Qualifier? = null, - noinline parameters: ParametersDefinition? = null -): Lazy = lazy { getViewModel(qualifier, parameters) } - /** * Get a viewModel instance * @@ -85,9 +76,62 @@ fun LifecycleOwner.getViewModel( return getKoin().getViewModel( ViewModelParameters( clazz, + null, this@getViewModel, qualifier, parameters = parameters ) ) } + +/** + * Lazy getByClass a viewModel instance + * + * @param qualifier - Koin BeanDefinition qualifier (if have several ViewModel beanDefinition of the same type) + * @param parameters - parameters to pass to the BeanDefinition + */ +inline fun LifecycleOwner.viewModel( + qualifier: Qualifier? = null, + noinline parameters: ParametersDefinition? = null +): Lazy = lazy { getViewModel(qualifier, parameters) } + + +//saved state + +fun LifecycleOwner.getViewModelWithState( + clazz: KClass, + defaultArguments: Bundle?, + qualifier: Qualifier? = null, + parameters: ParametersDefinition? = null +): T { + return getKoin().getViewModelWithState( + ViewModelParameters( + clazz, + defaultArguments, + this@getViewModelWithState, + qualifier, + parameters = parameters + ) + ) +} + +inline fun LifecycleOwner.getViewModelWithState( + defaultArguments: Bundle?, + qualifier: Qualifier? = null, + noinline parameters: ParametersDefinition? = null +): T { + return getViewModelWithState(T::class, defaultArguments, qualifier, parameters) +} + +fun LifecycleOwner.viewModelWithState( + clazz: KClass, + qualifier: Qualifier? = null, + defaultArguments: Bundle? = null, + parameters: ParametersDefinition? = null +): Lazy = lazy { getViewModelWithState(clazz, defaultArguments, qualifier, parameters) } + +inline fun LifecycleOwner.viewModelWithState( + qualifier: Qualifier? = null, + defaultArguments: Bundle? = null, + noinline parameters: ParametersDefinition? = null +): Lazy = lazy { getViewModelWithState(defaultArguments, qualifier, parameters) } \ No newline at end of file diff --git a/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ext/android/ScopeExt.kt b/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ext/android/ScopeExt.kt index eaf448b3d..8986396fa 100644 --- a/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ext/android/ScopeExt.kt +++ b/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel/ext/android/ScopeExt.kt @@ -15,10 +15,12 @@ */ package org.koin.androidx.viewmodel.ext.android +import android.os.Bundle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel import org.koin.androidx.viewmodel.ViewModelParameters import org.koin.androidx.viewmodel.getViewModel +import org.koin.androidx.viewmodel.getViewModelWithState import org.koin.core.parameter.ParametersDefinition import org.koin.core.qualifier.Qualifier import org.koin.core.scope.Scope @@ -86,6 +88,76 @@ fun Scope.getViewModel( return getViewModel( ViewModelParameters( clazz, + null, + owner, + qualifier, + parameters = parameters + ) + ) +} + +//saved state +/** + * Lazy get a viewModel instance + * + * @param qualifier - Koin BeanDefinition qualifier (if have several ViewModel beanDefinition of the same type) + * @param parameters - parameters to pass to the BeanDefinition + * @param clazz + */ +fun Scope.viewModelWithState( + owner: LifecycleOwner, + clazz: KClass, + qualifier: Qualifier? = null, + defaultArguments: Bundle?, + parameters: ParametersDefinition? = null +): Lazy = lazy { getViewModelWithState(owner, clazz, qualifier, defaultArguments, parameters) } + +/** + * Lazy getByClass a viewModel instance + * + * @param qualifier - Koin BeanDefinition qualifier (if have several ViewModel beanDefinition of the same type) + * @param parameters - parameters to pass to the BeanDefinition + */ +inline fun Scope.viewModelWithState( + owner: LifecycleOwner, + qualifier: Qualifier? = null, + defaultArguments: Bundle?, + noinline parameters: ParametersDefinition? = null +): Lazy = lazy { getViewModelWithState(owner, qualifier, defaultArguments, parameters) } + +/** + * Get a viewModel instance + * + * @param qualifier - Koin BeanDefinition qualifier (if have several ViewModel beanDefinition of the same type) + * @param parameters - parameters to pass to the BeanDefinition + */ +inline fun Scope.getViewModelWithState( + owner: LifecycleOwner, + qualifier: Qualifier? = null, + defaultArguments: Bundle?, + noinline parameters: ParametersDefinition? = null +): T { + return getViewModelWithState(owner, T::class, qualifier, defaultArguments, parameters) +} + +/** + * Lazy getByClass a viewModel instance + * + * @param clazz - Class of the BeanDefinition to retrieve + * @param qualifier - Koin BeanDefinition qualifier (if have several ViewModel beanDefinition of the same type) + * @param parameters - parameters to pass to the BeanDefinition + */ +fun Scope.getViewModelWithState( + owner: LifecycleOwner, + clazz: KClass, + qualifier: Qualifier? = null, + defaultArguments: Bundle?, + parameters: ParametersDefinition? = null +): T { + return getViewModelWithState( + ViewModelParameters( + clazz, + defaultArguments, owner, qualifier, parameters = parameters