mirror of
https://github.com/PhilKes/NotallyX.git
synced 2025-06-28 12:19:55 +00:00
Merge pull request #573 from PhilKes/fix/biometric-lock-open-note
Fix/biometric lock open note
This commit is contained in:
commit
fb35ffdac4
4 changed files with 63 additions and 174 deletions
|
@ -190,7 +190,7 @@ dependencies {
|
|||
implementation("androidx.security:security-crypto:1.1.0-alpha06")
|
||||
implementation("androidx.sqlite:sqlite-ktx:2.4.0")
|
||||
implementation("androidx.work:work-runtime:2.9.1")
|
||||
|
||||
implementation("androidx.biometric:biometric:1.1.0")
|
||||
implementation("cat.ereza:customactivityoncrash:2.4.0")
|
||||
implementation("com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0")
|
||||
implementation("com.github.bumptech.glide:glide:4.15.1")
|
||||
|
|
|
@ -64,7 +64,10 @@ abstract class LockedActivity<T : ViewBinding> : AppCompatActivity() {
|
|||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
if (preferences.biometricLock.value == BiometricLock.ENABLED) {
|
||||
if (
|
||||
preferences.biometricLock.value == BiometricLock.ENABLED &&
|
||||
notallyXApplication.locked.value
|
||||
) {
|
||||
hide()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.philkes.notallyx.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.KeyguardManager
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
|
@ -12,7 +11,6 @@ import android.content.ContentResolver
|
|||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.hardware.biometrics.BiometricManager
|
||||
import android.net.Uri
|
||||
|
@ -126,30 +124,15 @@ fun Context.getFileName(uri: Uri): String? =
|
|||
}
|
||||
|
||||
fun Context.canAuthenticateWithBiometrics(): Int {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
val keyguardManager = ContextCompat.getSystemService(this, KeyguardManager::class.java)
|
||||
val packageManager: PackageManager = this.packageManager
|
||||
if (!packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
|
||||
return BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
|
||||
}
|
||||
if (keyguardManager?.isKeyguardSecure == false) {
|
||||
return BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|
||||
}
|
||||
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
val biometricManager: BiometricManager =
|
||||
this.getSystemService(BiometricManager::class.java)
|
||||
return biometricManager.canAuthenticate()
|
||||
val biometricManager = androidx.biometric.BiometricManager.from(this)
|
||||
val authenticators =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG or
|
||||
androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
|
||||
} else {
|
||||
val biometricManager: BiometricManager =
|
||||
this.getSystemService(BiometricManager::class.java)
|
||||
return biometricManager.canAuthenticate(
|
||||
BiometricManager.Authenticators.BIOMETRIC_STRONG or
|
||||
BiometricManager.Authenticators.DEVICE_CREDENTIAL
|
||||
)
|
||||
androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
|
||||
}
|
||||
}
|
||||
return BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
|
||||
return biometricManager.canAuthenticate(authenticators)
|
||||
}
|
||||
|
||||
fun Context.getUriForFile(file: File): Uri =
|
||||
|
|
|
@ -4,15 +4,13 @@ import android.app.Activity
|
|||
import android.app.KeyguardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.hardware.biometrics.BiometricManager
|
||||
import android.hardware.biometrics.BiometricPrompt
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.os.Build
|
||||
import android.os.CancellationSignal
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.philkes.notallyx.R
|
||||
import javax.crypto.Cipher
|
||||
|
||||
|
@ -27,7 +25,7 @@ fun Activity.showBiometricOrPinPrompt(
|
|||
) {
|
||||
showBiometricOrPinPrompt(
|
||||
isForDecrypt,
|
||||
this,
|
||||
this as FragmentActivity,
|
||||
activityResultLauncher,
|
||||
titleResId,
|
||||
descriptionResId,
|
||||
|
@ -48,7 +46,7 @@ fun Fragment.showBiometricOrPinPrompt(
|
|||
) {
|
||||
showBiometricOrPinPrompt(
|
||||
isForDecrypt,
|
||||
requireContext(),
|
||||
activity!!,
|
||||
activityResultLauncher,
|
||||
titleResId,
|
||||
descriptionResId,
|
||||
|
@ -60,7 +58,7 @@ fun Fragment.showBiometricOrPinPrompt(
|
|||
|
||||
private fun showBiometricOrPinPrompt(
|
||||
isForDecrypt: Boolean,
|
||||
context: Context,
|
||||
context: FragmentActivity,
|
||||
activityResultLauncher: ActivityResultLauncher<Intent>,
|
||||
titleResId: Int,
|
||||
descriptionResId: Int? = null,
|
||||
|
@ -69,142 +67,55 @@ private fun showBiometricOrPinPrompt(
|
|||
onFailure: (errorCode: Int?) -> Unit,
|
||||
) {
|
||||
when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
|
||||
// Android 11+ with BiometricPrompt and Authenticators
|
||||
val prompt =
|
||||
BiometricPrompt.Builder(context)
|
||||
.apply {
|
||||
setTitle(context.getString(titleResId))
|
||||
descriptionResId?.let {
|
||||
setDescription(context.getString(descriptionResId))
|
||||
}
|
||||
setAllowedAuthenticators(
|
||||
BiometricManager.Authenticators.BIOMETRIC_STRONG or
|
||||
BiometricManager.Authenticators.DEVICE_CREDENTIAL
|
||||
)
|
||||
}
|
||||
.build()
|
||||
val cipher =
|
||||
if (isForDecrypt) {
|
||||
getInitializedCipherForDecryption(iv = cipherIv!!)
|
||||
} else {
|
||||
getInitializedCipherForEncryption()
|
||||
}
|
||||
prompt.authenticate(
|
||||
BiometricPrompt.CryptoObject(cipher),
|
||||
getCancellationSignal(context),
|
||||
context.mainExecutor,
|
||||
object : BiometricPrompt.AuthenticationCallback() {
|
||||
override fun onAuthenticationSucceeded(
|
||||
result: BiometricPrompt.AuthenticationResult?
|
||||
) {
|
||||
super.onAuthenticationSucceeded(result)
|
||||
onSuccess.invoke(result!!.cryptoObject!!.cipher)
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
super.onAuthenticationFailed()
|
||||
onFailure.invoke(null)
|
||||
}
|
||||
|
||||
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
|
||||
super.onAuthenticationError(errorCode, errString)
|
||||
onFailure.invoke(errorCode)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
Build.VERSION.SDK_INT == Build.VERSION_CODES.Q -> {
|
||||
// Android 10: Use BiometricPrompt without Authenticators
|
||||
val prompt =
|
||||
BiometricPrompt.Builder(context)
|
||||
.apply {
|
||||
setTitle(context.getString(titleResId))
|
||||
descriptionResId?.let {
|
||||
setDescription(context.getString(descriptionResId))
|
||||
}
|
||||
setNegativeButton(
|
||||
context.getString(R.string.cancel),
|
||||
context.mainExecutor,
|
||||
) { _, _ ->
|
||||
onFailure.invoke(null)
|
||||
}
|
||||
}
|
||||
.build()
|
||||
val cipher =
|
||||
if (isForDecrypt) {
|
||||
getInitializedCipherForDecryption(iv = cipherIv!!)
|
||||
} else {
|
||||
getInitializedCipherForEncryption()
|
||||
}
|
||||
prompt.authenticate(
|
||||
BiometricPrompt.CryptoObject(cipher),
|
||||
getCancellationSignal(context),
|
||||
context.mainExecutor,
|
||||
object : BiometricPrompt.AuthenticationCallback() {
|
||||
override fun onAuthenticationSucceeded(
|
||||
result: BiometricPrompt.AuthenticationResult?
|
||||
) {
|
||||
super.onAuthenticationSucceeded(result)
|
||||
onSuccess.invoke(result!!.cryptoObject!!.cipher)
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
super.onAuthenticationFailed()
|
||||
onFailure.invoke(null)
|
||||
}
|
||||
|
||||
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
|
||||
super.onAuthenticationError(errorCode, errString)
|
||||
onFailure.invoke(errorCode)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
|
||||
val fingerprintManager =
|
||||
ContextCompat.getSystemService(context, FingerprintManager::class.java)
|
||||
if (
|
||||
fingerprintManager?.isHardwareDetected == true &&
|
||||
fingerprintManager.hasEnrolledFingerprints()
|
||||
) {
|
||||
val cipher =
|
||||
if (isForDecrypt) {
|
||||
getInitializedCipherForDecryption(iv = cipherIv!!)
|
||||
} else {
|
||||
getInitializedCipherForEncryption()
|
||||
val promptInfo =
|
||||
BiometricPrompt.PromptInfo.Builder()
|
||||
.apply {
|
||||
setTitle(context.getString(titleResId))
|
||||
descriptionResId?.let {
|
||||
setDescription(context.getString(descriptionResId))
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
setAllowedAuthenticators(
|
||||
BiometricManager.Authenticators.BIOMETRIC_STRONG or
|
||||
BiometricManager.Authenticators.DEVICE_CREDENTIAL
|
||||
)
|
||||
} else {
|
||||
setNegativeButtonText(context.getString(R.string.cancel))
|
||||
setAllowedAuthenticators(
|
||||
BiometricManager.Authenticators.BIOMETRIC_STRONG
|
||||
)
|
||||
}
|
||||
}
|
||||
.build()
|
||||
val cipher =
|
||||
if (isForDecrypt) {
|
||||
getInitializedCipherForDecryption(iv = cipherIv!!)
|
||||
} else {
|
||||
getInitializedCipherForEncryption()
|
||||
}
|
||||
val authCallback =
|
||||
object : BiometricPrompt.AuthenticationCallback() {
|
||||
override fun onAuthenticationSucceeded(
|
||||
result: BiometricPrompt.AuthenticationResult
|
||||
) {
|
||||
super.onAuthenticationSucceeded(result)
|
||||
onSuccess.invoke(result.cryptoObject!!.cipher!!)
|
||||
}
|
||||
fingerprintManager.authenticate(
|
||||
FingerprintManager.CryptoObject(cipher),
|
||||
getCancellationSignal(context),
|
||||
0,
|
||||
object : FingerprintManager.AuthenticationCallback() {
|
||||
override fun onAuthenticationSucceeded(
|
||||
result: FingerprintManager.AuthenticationResult?
|
||||
) {
|
||||
super.onAuthenticationSucceeded(result)
|
||||
onSuccess.invoke(result!!.cryptoObject!!.cipher)
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
super.onAuthenticationFailed()
|
||||
onFailure.invoke(null)
|
||||
}
|
||||
override fun onAuthenticationFailed() {
|
||||
super.onAuthenticationFailed()
|
||||
onFailure.invoke(null)
|
||||
}
|
||||
|
||||
override fun onAuthenticationError(
|
||||
errorCode: Int,
|
||||
errString: CharSequence?,
|
||||
) {
|
||||
super.onAuthenticationError(errorCode, errString)
|
||||
onFailure.invoke(errorCode)
|
||||
}
|
||||
},
|
||||
null,
|
||||
)
|
||||
} else {
|
||||
promptPinAuthentication(context, activityResultLauncher, titleResId, onFailure)
|
||||
}
|
||||
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
|
||||
super.onAuthenticationError(errorCode, errString)
|
||||
onFailure.invoke(errorCode)
|
||||
}
|
||||
}
|
||||
val prompt =
|
||||
BiometricPrompt(context, ContextCompat.getMainExecutor(context), authCallback)
|
||||
prompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
@ -214,14 +125,6 @@ private fun showBiometricOrPinPrompt(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getCancellationSignal(context: Context): CancellationSignal {
|
||||
return CancellationSignal().apply {
|
||||
setOnCancelListener {
|
||||
Toast.makeText(context, R.string.biometrics_failure, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun promptPinAuthentication(
|
||||
context: Context,
|
||||
activityResultLauncher: ActivityResultLauncher<Intent>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue