android / codelab-android-hilt

Apache License 2.0
355 stars 188 forks source link

SavedStateViewModelFactory constructed with empty constructor supports only calls to create(modelClass: Class<T>, extras: CreationExtras). #42

Closed sebastinto closed 2 years ago

sebastinto commented 2 years ago

I am getting SavedStateViewModelFactory constructed with empty constructor supports only calls to create(modelClass: Class, extras: CreationExtras) errors when createBiometricPrompt() is called in the code below:

@HiltAndroidApp
class OPBApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        // Logging
        Timber.plant(Timber.DebugTree())
    }
}
@OptIn(
    ExperimentalAnimationApi::class,
    ExperimentalMaterialApi::class,
    ExperimentalComposeUiApi::class,
    ExperimentalFoundationApi::class
)
@AndroidEntryPoint
class MainActivity : FragmentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        WindowCompat.setDecorFitsSystemWindows(window, false)
        setContent {
            MyTheme() {
                ProvideWindowInsets {
                    Navigation()
                }
            }
        }
    }
@OptIn(
    ExperimentalAnimationApi::class,
    ExperimentalMaterialApi::class,
    ExperimentalComposeUiApi::class,
    ExperimentalFoundationApi::class
)
@Composable
fun Navigation(
   // other params...
    authViewModel: AuthViewModel = hiltViewModel(),
   // other params...
) {

    Root( ) { scaffoldState, coroutineScope ->
        AnimatedNavHost(
            navController = navController,
            startDestination = //...
        ) {

            composable(route = Screen.Auth.name) {
                // fixme: this crashes with `SavedStateViewModelFactory constructed with empty constructor supports only calls to create(modelClass: Class, extras: CreationExtras)`
                BiometricPromptDialog(
                    biometricPrompt = authViewModel.createBiometricPrompt(
                        activity = LocalContext.current as FragmentActivity,
                        onSuccess = {
                            navController.navigate(/* some screen */) {
                                // remove biometric screen from back stack
                                popUpTo(/* some screen *) {
                                    inclusive = true
                                }
                            }
                        }
                    ),
                      // other params...
            }
            // other composable()
        }
    }
}
@HiltViewModel
class AuthViewModel @Inject constructor(
    @BiometricSupported isBiometricAuthSupported: Boolean,
    @IoDispatcher private val ioDispatcher: CoroutineDispatcher,
    private val preferenceDataStore: PreferenceDataStore
) : ViewModel() {

// other code...

fun createBiometricPrompt(
        activity: FragmentActivity,
        onSuccess: (BiometricPrompt.AuthenticationResult) -> Unit
    ): BiometricPrompt {
        val executor = ContextCompat.getMainExecutor(activity)

        val callback = object : BiometricPrompt.AuthenticationCallback() {
            override fun onAuthenticationError(errCode: Int, errString: CharSequence) {
                super.onAuthenticationError(errCode, errString)
                Timber.e("Error authenticating. Code $errCode ($errString")
                promptState = BiometricPromptState.ERROR
            }

            override fun onAuthenticationFailed() {
                super.onAuthenticationFailed()
                Timber.e("Biometric authentication failed.")
                promptState = BiometricPromptState.FAIL
            }

            override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                super.onAuthenticationSucceeded(result)
                Timber.d("Authentication successful")
                promptState = BiometricPromptState.SUCCESS
                onSuccess(result)
            }
        }
        return BiometricPrompt(activity, executor, callback)
    }
}
buildscript {

    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath("com.android.tools.build:gradle:7.1.1")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")
        classpath("com.google.dagger:hilt-android-gradle-plugin:2.40.5"
    }
}

tasks.register("clean", Delete::class) {
    delete(rootProject.buildDir)
}
plugins {
    id("com.android.application")
    kotlin("android")
    id("kotlin-parcelize")
    kotlin("kapt")
    id("dagger.hilt.android.plugin")
    id("com.google.protobuf") version "0.8.12"
    id("com.mikepenz.aboutlibraries.plugin") version "10.0.0-b08"
}

dependencies {
    // other dependencies...

    implementation("com.google.dagger:hilt-android:2.40.5")
    kapt("com.google.dagger:hilt-compiler:2.40.5}")
    kapt("androidx.hilt:hilt-compiler:1.0.0")
    implementation("androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03")
    implementation("androidx.hilt:hilt-navigation-compose:1.0.0")

    // other dependencies...
}

The error occurs also when creating an instance of AuthViewModel in MainActivity and passing it down as a parameter to Navigation() (instead of using hiltViewModel())

private val authViewModel by viewModels<AuthViewModel>()

Can't seem to figure this one out. What step can I take to find the source of the issue? Any pointer is appreciated, thanks!

Full crash log:

2022-02-06 22:35:29.957 31668-31668/com.my.package E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.my.package, PID: 31668
    java.lang.UnsupportedOperationException: SavedStateViewModelFactory constructed with empty constructor supports only calls to create(modelClass: Class<T>, extras: CreationExtras).
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:165)
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:220)
        at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.create(HiltViewModelFactory.java:111)
        at androidx.lifecycle.ViewModelProvider$Factory.create(ViewModelProvider.kt:79)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:168)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:137)
        at androidx.biometric.BiometricPrompt.getViewModel(BiometricPrompt.java:1029)
        at androidx.biometric.BiometricPrompt.<init>(BiometricPrompt.java:847)
        at com.my.package.viewmodel.AuthViewModel.createBiometricPrompt(AuthViewModel.kt:105)
        at com.my.package.navigation.NavigationKt$Navigation$13$1$1.invoke(Navigation.kt:165)
        at com.my.package.navigation.NavigationKt$Navigation$13$1$1.invoke(Navigation.kt:162)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:135)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at com.google.accompanist.navigation.animation.AnimatedNavHostKt$AnimatedNavHost$9$1.invoke(AnimatedNavHost.kt:212)
        at com.google.accompanist.navigation.animation.AnimatedNavHostKt$AnimatedNavHost$9$1.invoke(AnimatedNavHost.kt:210)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
        at androidx.compose.runtime.saveable.SaveableStateHolderImpl.SaveableStateProvider(SaveableStateHolder.kt:84)
        at androidx.navigation.compose.NavBackStackEntryProviderKt.SaveableStateProvider(NavBackStackEntryProvider.kt:60)
        at androidx.navigation.compose.NavBackStackEntryProviderKt.access$SaveableStateProvider(NavBackStackEntryProvider.kt:1)
        at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider$1.invoke(NavBackStackEntryProvider.kt:52)
        at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider$1.invoke(NavBackStackEntryProvider.kt:51)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
        at androidx.navigation.compose.NavBackStackEntryProviderKt.LocalOwnersProvider(NavBackStackEntryProvider.kt:47)
        at com.google.accompanist.navigation.animation.AnimatedNavHostKt$AnimatedNavHost$9.invoke(AnimatedNavHost.kt:210)
        at com.google.accompanist.navigation.animation.AnimatedNavHostKt$AnimatedNavHost$9.invoke(AnimatedNavHost.kt:202)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:135)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.animation.AnimatedContentKt$AnimatedContent$5$1$4.invoke(AnimatedContent.kt:658)
        at androidx.compose.animation.AnimatedContentKt$AnimatedContent$5$1$4.invoke(AnimatedContent.kt:648)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.animation.AnimatedVisibilityKt.AnimatedEnterExitImpl(AnimatedVisibility.kt:935)
        at androidx.compose.animation.AnimatedVisibilityKt.AnimatedVisibility(AnimatedVisibility.kt:606)
2022-02-06 22:35:29.958 31668-31668/com.my.package E/AndroidRuntime:     at androidx.compose.animation.AnimatedContentKt$AnimatedContent$5$1.invoke(AnimatedContent.kt:638)
        at androidx.compose.animation.AnimatedContentKt$AnimatedContent$5$1.invoke(AnimatedContent.kt:625)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.animation.AnimatedContentKt.AnimatedContent(AnimatedContent.kt:671)
        at com.google.accompanist.navigation.animation.AnimatedNavHostKt.AnimatedNavHost(AnimatedNavHost.kt:197)
        at com.google.accompanist.navigation.animation.AnimatedNavHostKt.AnimatedNavHost(AnimatedNavHost.kt:91)
        at com.my.package.navigation.NavigationKt$Navigation$13.invoke(Navigation.kt:151)
        at com.my.package.navigation.NavigationKt$Navigation$13.invoke(Navigation.kt:150)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:135)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at com.my.package.ui.components.RootKt$Root$3$4.invoke(Root.kt:215)
        at com.my.package.ui.components.RootKt$Root$3$4.invoke(Root.kt:214)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
        at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:127)
        at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:127)
        at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:142)
        at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2158)
        at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2413)
        at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2594)
        at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2580)
        at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:247)
        at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(Unknown Source:1)
        at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:2580)
        at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:2556)
        at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:624)
        at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:795)
        at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:106)
        at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:465)
        at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:434)
        at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:34)
        at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
        at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
        at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:947)
        at android.view.Choreographer.doCallbacks(Choreographer.java:761)
        at android.view.Choreographer.doFrame(Choreographer.java:693)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:935)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
sebastinto commented 2 years ago

wrong repo my bad