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

fix: SavedStateProvider with the given key is already registered #21

Merged
merged 1 commit into from
Dec 16, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
24 changes: 12 additions & 12 deletions voyager-hilt/src/main/java/cafe/adriel/voyager/hilt/ViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@ import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import cafe.adriel.voyager.androidx.AndroidScreenLifecycleOwner
import cafe.adriel.voyager.core.lifecycle.ScreenLifecycleProvider
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.hilt.internal.componentActivity
import cafe.adriel.voyager.hilt.internal.defaultViewModelProviderFactory

/**
* A function to provide a [dagger.hilt.android.lifecycle.HiltViewModel] managed by voyager ViewModelStore
* instead of using Activity ViewModelStore.
* There is compatibility with Activity ViewModelStore too but it must be avoided because your ViewModels
* A function to provide a [dagger.hilt.android.lifecycle.HiltViewModel] managed by voyager ViewModelLifecycleOwner
* instead of using Activity ViewModelLifecycleOwner.
* There is compatibility with Activity ViewModelLifecycleOwner too but it must be avoided because your ViewModels
* will be cleared when activity is totally destroyed only.
*
* @param viewModelProviderFactory A custom factory commonly used with Assisted Injection
Expand All @@ -26,14 +25,15 @@ public inline fun <reified T : ViewModel> Screen.getViewModel(
viewModelProviderFactory: ViewModelProvider.Factory? = null
): T {
val context = LocalContext.current
val factory = viewModelProviderFactory ?: context.defaultViewModelProviderFactory
return remember(key1 = T::class) {
val viewModelStore = when (this) {
is ScreenLifecycleProvider ->
(this.getLifecycleOwner() as? ViewModelStoreOwner)?.viewModelStore
?: error("LifecycleOwner provided by your Screen must be an androidx.lifecycle.ViewModelStoreOwner")
else -> context.componentActivity.viewModelStore
}
val activity = context.componentActivity
val lifecycleOwner = (this as? ScreenLifecycleProvider)?.getLifecycleOwner() as? AndroidScreenLifecycleOwner
val viewModelStore = lifecycleOwner?.viewModelStore ?: activity.viewModelStore
val factory = VoyagerHiltViewModelFactories.getVoyagerFactory(
activity = activity,
owner = lifecycleOwner ?: activity,
delegateFactory = viewModelProviderFactory
)
val provider = ViewModelProvider(store = viewModelStore, factory = factory)
provider[T::class.java]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package cafe.adriel.voyager.hilt

import android.app.Application
import androidx.activity.ComponentActivity
import androidx.lifecycle.SavedStateViewModelFactory
import androidx.lifecycle.ViewModelProvider
import androidx.savedstate.SavedStateRegistryOwner
import dagger.hilt.EntryPoint
import dagger.hilt.EntryPoints
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
import dagger.hilt.android.internal.builders.ViewModelComponentBuilder
import dagger.hilt.android.internal.lifecycle.HiltViewModelFactory
import dagger.hilt.android.internal.lifecycle.HiltViewModelMap
import javax.inject.Inject

public object VoyagerHiltViewModelFactories {

public fun getVoyagerFactory(
activity: ComponentActivity,
owner: SavedStateRegistryOwner,
delegateFactory: ViewModelProvider.Factory?
): ViewModelProvider.Factory {
return EntryPoints.get(activity, ViewModelFactoryEntryPoint::class.java)
.internalViewModelFactory()
.fromActivity(activity, owner, delegateFactory)
}

internal class InternalViewModelFactory @Inject internal constructor(
private val application: Application,
@HiltViewModelMap.KeySet private val keySet: Set<String>,
private val viewModelComponentBuilder: ViewModelComponentBuilder
) {
fun fromActivity(
activity: ComponentActivity,
owner: SavedStateRegistryOwner,
delegateFactory: ViewModelProvider.Factory?
): ViewModelProvider.Factory {
val defaultArgs = activity.intent?.extras
val delegate = delegateFactory ?: SavedStateViewModelFactory(application, owner, defaultArgs)
return HiltViewModelFactory(owner, defaultArgs, keySet, delegate, viewModelComponentBuilder)
}
}

@EntryPoint
@InstallIn(ActivityComponent::class)
internal interface ViewModelFactoryEntryPoint {
fun internalViewModelFactory(): InternalViewModelFactory
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
package cafe.adriel.voyager.hilt.internal

import android.content.Context
import androidx.activity.ComponentActivity
import androidx.lifecycle.ViewModelProvider

@PublishedApi
internal val Context.componentActivity: ComponentActivity
get() = this as? ComponentActivity
?: error("Invalid local context. It must be a ComponentActivity")

@PublishedApi
internal val Context.defaultViewModelProviderFactory: ViewModelProvider.Factory
get() = componentActivity.defaultViewModelProviderFactory
package cafe.adriel.voyager.hilt.internal

import android.content.Context
import androidx.activity.ComponentActivity

@PublishedApi
internal val Context.componentActivity: ComponentActivity
get() = this as? ComponentActivity
?: error("Invalid local context. It must be a ComponentActivity")