2

I'm using Biometrics library to lock the app. Everything works fine and when I'm unlocking with fingerprint onAuthenticationSucceeded() get's called and device navigates from the lock screen. However if unlock with pattern the onAuthenticationSucceeded() get's called but navigation doesn't initialise and I'm left stuck on the lock screen fragment.

EDIT: This only affects API29 with ANY device credentials

EDIT2: I'm also getting

FragmentNavigator: Ignoring popBackStack() call: FragmentManager has already saved its state

FragmentNavigator: Ignoring navigate() call: FragmentManager has already saved its state

private lateinit var biometricPrompt: BiometricPrompt

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    biometricPrompt = createBiometricPrompt()
    return inflater.inflate(R.layout.lock_screen_fragment, container, false)
}

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

    val isAppLockEnabled: Boolean = PreferenceManager.getDefaultSharedPreferences(context)
        .getBoolean("lock_app_preference", false)

    // If app locks is not set go to home fragment else display app lock screen
    if (!isAppLockEnabled) {
        findNavController().navigate(R.id.action_lock_screen_fragment_dest_to_home_fragment_dest)
    } else {

   

        // Prompt appears when user clicks "Unlock".
        unlock_button.setOnClickListener {
            val promptInfo = createPromptInfo()
            biometricPrompt.authenticate(promptInfo)
        }
   }

}

private fun createBiometricPrompt(): BiometricPrompt {
    val executor = ContextCompat.getMainExecutor(context)

    val callback = object : BiometricPrompt.AuthenticationCallback() {
        override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
            super.onAuthenticationError(errorCode, errString)
            Log.d("AuthenticationError()", "$errorCode :: $errString")
        }

        override fun onAuthenticationFailed() {
            super.onAuthenticationFailed()
            Log.d("AuthenticationFailed()", "Authentication failed for an unknown reason")
        }

        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
            super.onAuthenticationSucceeded(result)
            lock_icon.setImageResource(R.drawable.ic_unlock)
            lock_screen_text_view.text = getString(R.string.app_unlocked)
        //This doesn't work when using pattern unlock   findNavController().navigate(R.id.action_lock_screen_fragment_dest_to_home_fragment_dest)
        }
    }

    return BiometricPrompt(this, executor, callback)
}

private fun createPromptInfo(): BiometricPrompt.PromptInfo {
    return BiometricPrompt.PromptInfo.Builder()
        .setTitle("Unlock App")
        .setConfirmationRequired(false)
        .setDeviceCredentialAllowed(true)
        .build()
}

}
Community
  • 1
  • 1
MakinTosH
  • 623
  • 7
  • 12

1 Answers1

3

Ok, so I solved this issue. Moved navigation from onAuthenticationSucceeded() to fragments onResume(). Device credentials window pauses my app and somehow navigation cannot be called after that.

Solution code:

private var isAppUnlocked : Boolean = false    


override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
            super.onAuthenticationSucceeded(result)
            isAppUnlocked = true
            unlockApp()
            Log.d("AuthenticationSuccess", "Authentication succeeded")
        }    

override fun onResume() {
    super.onResume()
    if(isAppUnlocked){
        unlockApp()
    }
}    

private fun unlockApp(){
    lock_icon.setImageResource(R.drawable.ic_unlock)
    lock_screen_text_view.text = getString(R.string.app_unlocked)
    findNavController().navigate(R.id.action_lock_screen_fragment_dest_to_home_fragment_dest)
}    
MakinTosH
  • 623
  • 7
  • 12