|
|
@ -17,6 +17,7 @@ import android.util.Log
|
|
|
|
import androidx.appcompat.app.AlertDialog
|
|
|
|
import androidx.appcompat.app.AlertDialog
|
|
|
|
import androidx.biometric.BiometricManager
|
|
|
|
import androidx.biometric.BiometricManager
|
|
|
|
import androidx.biometric.BiometricPrompt
|
|
|
|
import androidx.biometric.BiometricPrompt
|
|
|
|
|
|
|
|
import androidx.core.content.ContextCompat
|
|
|
|
import androidx.lifecycle.lifecycleScope
|
|
|
|
import androidx.lifecycle.lifecycleScope
|
|
|
|
import androidx.navigation.fragment.findNavController
|
|
|
|
import androidx.navigation.fragment.findNavController
|
|
|
|
import androidx.preference.Preference
|
|
|
|
import androidx.preference.Preference
|
|
|
@ -31,6 +32,7 @@ import mozilla.components.concept.sync.OAuthAccount
|
|
|
|
import mozilla.components.service.fxa.SyncEngine
|
|
|
|
import mozilla.components.service.fxa.SyncEngine
|
|
|
|
import mozilla.components.service.fxa.manager.SyncEnginesStorage
|
|
|
|
import mozilla.components.service.fxa.manager.SyncEnginesStorage
|
|
|
|
import org.mozilla.fenix.R
|
|
|
|
import org.mozilla.fenix.R
|
|
|
|
|
|
|
|
import org.mozilla.fenix.addons.runIfFragmentIsAttached
|
|
|
|
import org.mozilla.fenix.components.metrics.Event
|
|
|
|
import org.mozilla.fenix.components.metrics.Event
|
|
|
|
import org.mozilla.fenix.ext.components
|
|
|
|
import org.mozilla.fenix.ext.components
|
|
|
|
import org.mozilla.fenix.ext.requireComponents
|
|
|
|
import org.mozilla.fenix.ext.requireComponents
|
|
|
@ -39,7 +41,7 @@ import org.mozilla.fenix.ext.settings
|
|
|
|
import org.mozilla.fenix.ext.showToolbar
|
|
|
|
import org.mozilla.fenix.ext.showToolbar
|
|
|
|
import org.mozilla.fenix.settings.SharedPreferenceUpdater
|
|
|
|
import org.mozilla.fenix.settings.SharedPreferenceUpdater
|
|
|
|
import org.mozilla.fenix.settings.requirePreference
|
|
|
|
import org.mozilla.fenix.settings.requirePreference
|
|
|
|
import java.util.concurrent.Executors
|
|
|
|
import java.util.concurrent.Executor
|
|
|
|
|
|
|
|
|
|
|
|
@Suppress("TooManyFunctions", "LargeClass")
|
|
|
|
@Suppress("TooManyFunctions", "LargeClass")
|
|
|
|
class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|
|
|
class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|
|
@ -48,7 +50,7 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|
|
|
private lateinit var biometricPromptCallback: BiometricPrompt.AuthenticationCallback
|
|
|
|
private lateinit var biometricPromptCallback: BiometricPrompt.AuthenticationCallback
|
|
|
|
|
|
|
|
|
|
|
|
@TargetApi(M)
|
|
|
|
@TargetApi(M)
|
|
|
|
private val executor = Executors.newSingleThreadExecutor()
|
|
|
|
private lateinit var executor: Executor
|
|
|
|
|
|
|
|
|
|
|
|
@TargetApi(M)
|
|
|
|
@TargetApi(M)
|
|
|
|
private lateinit var biometricPrompt: BiometricPrompt
|
|
|
|
private lateinit var biometricPrompt: BiometricPrompt
|
|
|
@ -60,6 +62,28 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|
|
|
setPreferencesFromResource(R.xml.logins_preferences, rootKey)
|
|
|
|
setPreferencesFromResource(R.xml.logins_preferences, rootKey)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* There is a bug where while the biometric prompt is showing, you were able to quickly navigate
|
|
|
|
|
|
|
|
* so we are disabling the settings that navigate while authenticating.
|
|
|
|
|
|
|
|
* https://github.com/mozilla-mobile/fenix/issues/12312
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private fun togglePrefsEnabledWhileAuthenticating(enabled: Boolean) {
|
|
|
|
|
|
|
|
requirePreference<Preference>(R.string.pref_key_password_sync_logins).isEnabled = enabled
|
|
|
|
|
|
|
|
requirePreference<Preference>(R.string.pref_key_save_logins_settings).isEnabled = enabled
|
|
|
|
|
|
|
|
requirePreference<Preference>(R.string.pref_key_saved_logins).isEnabled = enabled
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun navigateToSavedLogins() {
|
|
|
|
|
|
|
|
runIfFragmentIsAttached {
|
|
|
|
|
|
|
|
viewLifecycleOwner.lifecycleScope.launch(Main) {
|
|
|
|
|
|
|
|
// Workaround for likely biometric library bug
|
|
|
|
|
|
|
|
// https://github.com/mozilla-mobile/fenix/issues/8438
|
|
|
|
|
|
|
|
delay(SHORT_DELAY_MS)
|
|
|
|
|
|
|
|
navigateToSavedLoginsFragment()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
|
|
|
|
|
|
|
@ -67,23 +91,22 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|
|
|
|
|
|
|
|
|
|
|
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
|
|
|
|
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
|
|
|
|
Log.e(LOG_TAG, "onAuthenticationError $errString")
|
|
|
|
Log.e(LOG_TAG, "onAuthenticationError $errString")
|
|
|
|
|
|
|
|
togglePrefsEnabledWhileAuthenticating(enabled = true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
|
|
|
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
|
|
|
Log.d(LOG_TAG, "onAuthenticationSucceeded")
|
|
|
|
Log.d(LOG_TAG, "onAuthenticationSucceeded")
|
|
|
|
viewLifecycleOwner.lifecycleScope.launch(Main) {
|
|
|
|
navigateToSavedLogins()
|
|
|
|
// Workaround for likely biometric library bug
|
|
|
|
|
|
|
|
// https://github.com/mozilla-mobile/fenix/issues/8438
|
|
|
|
|
|
|
|
delay(SHORT_DELAY_MS)
|
|
|
|
|
|
|
|
navigateToSavedLoginsFragment()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override fun onAuthenticationFailed() {
|
|
|
|
override fun onAuthenticationFailed() {
|
|
|
|
Log.e(LOG_TAG, "onAuthenticationFailed")
|
|
|
|
Log.e(LOG_TAG, "onAuthenticationFailed")
|
|
|
|
|
|
|
|
togglePrefsEnabledWhileAuthenticating(enabled = true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
executor = ContextCompat.getMainExecutor(requireContext())
|
|
|
|
|
|
|
|
|
|
|
|
biometricPrompt = BiometricPrompt(this, executor, biometricPromptCallback)
|
|
|
|
biometricPrompt = BiometricPrompt(this, executor, biometricPromptCallback)
|
|
|
|
|
|
|
|
|
|
|
|
promptInfo = BiometricPrompt.PromptInfo.Builder()
|
|
|
|
promptInfo = BiometricPrompt.PromptInfo.Builder()
|
|
|
@ -122,6 +145,7 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|
|
|
|
|
|
|
|
|
|
|
requirePreference<Preference>(R.string.pref_key_saved_logins).setOnPreferenceClickListener {
|
|
|
|
requirePreference<Preference>(R.string.pref_key_saved_logins).setOnPreferenceClickListener {
|
|
|
|
if (Build.VERSION.SDK_INT >= M && isHardwareAvailable && hasBiometricEnrolled) {
|
|
|
|
if (Build.VERSION.SDK_INT >= M && isHardwareAvailable && hasBiometricEnrolled) {
|
|
|
|
|
|
|
|
togglePrefsEnabledWhileAuthenticating(enabled = false)
|
|
|
|
biometricPrompt.authenticate(promptInfo)
|
|
|
|
biometricPrompt.authenticate(promptInfo)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
verifyPinOrShowSetupWarning()
|
|
|
|
verifyPinOrShowSetupWarning()
|
|
|
@ -148,7 +172,7 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|
|
|
|
|
|
|
|
|
|
|
override fun onAuthenticationProblems() = updateSyncPreferenceNeedsReauth()
|
|
|
|
override fun onAuthenticationProblems() = updateSyncPreferenceNeedsReauth()
|
|
|
|
|
|
|
|
|
|
|
|
val isHardwareAvailable: Boolean by lazy {
|
|
|
|
private val isHardwareAvailable: Boolean by lazy {
|
|
|
|
if (Build.VERSION.SDK_INT >= M) {
|
|
|
|
if (Build.VERSION.SDK_INT >= M) {
|
|
|
|
context?.let {
|
|
|
|
context?.let {
|
|
|
|
val bm = BiometricManager.from(it)
|
|
|
|
val bm = BiometricManager.from(it)
|
|
|
@ -161,7 +185,7 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
val hasBiometricEnrolled: Boolean by lazy {
|
|
|
|
private val hasBiometricEnrolled: Boolean by lazy {
|
|
|
|
if (Build.VERSION.SDK_INT >= M) {
|
|
|
|
if (Build.VERSION.SDK_INT >= M) {
|
|
|
|
context?.let {
|
|
|
|
context?.let {
|
|
|
|
val bm = BiometricManager.from(it)
|
|
|
|
val bm = BiometricManager.from(it)
|
|
|
@ -252,7 +276,8 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|
|
|
getString(R.string.logins_biometric_prompt_message_pin),
|
|
|
|
getString(R.string.logins_biometric_prompt_message_pin),
|
|
|
|
getString(R.string.logins_biometric_prompt_message)
|
|
|
|
getString(R.string.logins_biometric_prompt_message)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
startActivityForResult(intent,
|
|
|
|
startActivityForResult(
|
|
|
|
|
|
|
|
intent,
|
|
|
|
PIN_REQUEST
|
|
|
|
PIN_REQUEST
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -267,7 +292,8 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|
|
|
|
|
|
|
|
|
|
|
private fun navigateToSavedLoginsFragment() {
|
|
|
|
private fun navigateToSavedLoginsFragment() {
|
|
|
|
context?.components?.analytics?.metrics?.track(Event.OpenLogins)
|
|
|
|
context?.components?.analytics?.metrics?.track(Event.OpenLogins)
|
|
|
|
val directions = SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToLoginsListFragment()
|
|
|
|
val directions =
|
|
|
|
|
|
|
|
SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToLoginsListFragment()
|
|
|
|
findNavController().navigate(directions)
|
|
|
|
findNavController().navigate(directions)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -283,7 +309,8 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun navigateToTurnOnSyncFragment() {
|
|
|
|
private fun navigateToTurnOnSyncFragment() {
|
|
|
|
val directions = SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToTurnOnSyncFragment()
|
|
|
|
val directions =
|
|
|
|
|
|
|
|
SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToTurnOnSyncFragment()
|
|
|
|
findNavController().navigate(directions)
|
|
|
|
findNavController().navigate(directions)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|