|
|
|
@ -10,19 +10,31 @@ import androidx.lifecycle.ViewModel
|
|
|
|
|
import androidx.lifecycle.ViewModelProvider
|
|
|
|
|
import androidx.lifecycle.ViewModelStoreOwner
|
|
|
|
|
import androidx.lifecycle.get
|
|
|
|
|
import androidx.lifecycle.viewModelScope
|
|
|
|
|
import kotlinx.coroutines.CoroutineScope
|
|
|
|
|
import mozilla.components.lib.state.Store
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generic ViewModel to wrap a State object for state restoration.
|
|
|
|
|
*
|
|
|
|
|
* @property store [Store] instance attached to [ViewModel].
|
|
|
|
|
* @param createStore Function that creates a [Store] instance that receives [CoroutineScope] param
|
|
|
|
|
* that's tied to the lifecycle of the [StoreProvider] i.e it will cancel when
|
|
|
|
|
* [StoreProvider.onCleared] is called.
|
|
|
|
|
*/
|
|
|
|
|
class StoreProvider<T : Store<*, *>>(
|
|
|
|
|
val store: T,
|
|
|
|
|
createStore: (CoroutineScope) -> T,
|
|
|
|
|
) : ViewModel() {
|
|
|
|
|
|
|
|
|
|
@VisibleForTesting
|
|
|
|
|
internal val store: T = createStore(viewModelScope)
|
|
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
|
fun <T : Store<*, *>> get(owner: ViewModelStoreOwner, createStore: () -> T): T {
|
|
|
|
|
/**
|
|
|
|
|
* Returns an existing [Store] instance or creates a new one scoped to a
|
|
|
|
|
* [ViewModelStoreOwner].
|
|
|
|
|
* @see [ViewModelProvider.get].
|
|
|
|
|
*/
|
|
|
|
|
fun <T : Store<*, *>> get(owner: ViewModelStoreOwner, createStore: (CoroutineScope) -> T): T {
|
|
|
|
|
val factory = StoreProviderFactory(createStore)
|
|
|
|
|
val viewModel: StoreProvider<T> = ViewModelProvider(owner, factory).get()
|
|
|
|
|
return viewModel.store
|
|
|
|
@ -37,23 +49,39 @@ class StoreProvider<T : Store<*, *>>(
|
|
|
|
|
*/
|
|
|
|
|
@VisibleForTesting
|
|
|
|
|
class StoreProviderFactory<T : Store<*, *>>(
|
|
|
|
|
private val createStore: () -> T,
|
|
|
|
|
private val createStore: (CoroutineScope) -> T,
|
|
|
|
|
) : ViewModelProvider.Factory {
|
|
|
|
|
|
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
|
|
|
override fun <VM : ViewModel> create(modelClass: Class<VM>): VM {
|
|
|
|
|
return StoreProvider(createStore()) as VM
|
|
|
|
|
return StoreProvider(createStore) as VM
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper function for lazy creation of a [Store] instance scoped to a [ViewModelStoreOwner].
|
|
|
|
|
*
|
|
|
|
|
* @param createStore Function that creates a [Store] instance.
|
|
|
|
|
* @param createStore Function that creates a [Store] instance that receives [CoroutineScope] param
|
|
|
|
|
* that's tied to the lifecycle of the [StoreProvider] i.e it will cancel when
|
|
|
|
|
* [StoreProvider.onCleared] is called.
|
|
|
|
|
*
|
|
|
|
|
* Example:
|
|
|
|
|
* ```
|
|
|
|
|
* val store by lazy { scope ->
|
|
|
|
|
* MyStore(
|
|
|
|
|
* middleware = listOf(
|
|
|
|
|
* MyMiddleware(
|
|
|
|
|
* settings = requireComponents.settings,
|
|
|
|
|
* ...
|
|
|
|
|
* scope = scope,
|
|
|
|
|
* ),
|
|
|
|
|
* )
|
|
|
|
|
* )
|
|
|
|
|
* }
|
|
|
|
|
*/
|
|
|
|
|
@MainThread
|
|
|
|
|
fun <T : Store<*, *>> ViewModelStoreOwner.lazyStore(
|
|
|
|
|
createStore: () -> T,
|
|
|
|
|
createStore: (CoroutineScope) -> T,
|
|
|
|
|
): Lazy<T> {
|
|
|
|
|
return lazy(mode = LazyThreadSafetyMode.NONE) {
|
|
|
|
|
StoreProvider.get(this, createStore)
|
|
|
|
|