From 67d29ea633abbcc4f195131ad25eae2e7a192117 Mon Sep 17 00:00:00 2001 From: Raymond Lai Date: Sat, 13 Jul 2024 23:53:47 +0800 Subject: [PATCH] Migrate fingerprint decryption dialog to use AndroidX Biometrics API --- app/build.gradle | 1 + .../filesystem/files/EncryptDecryptUtils.java | 268 --------------- .../filesystem/files/EncryptDecryptUtils.kt | 315 ++++++++++++++++++ .../ui/dialogs/DecryptFingerprintDialog.kt | 43 ++- .../ui/fragments/MainFragment.java | 4 +- .../filemanager/utils/FingerprintHandler.java | 88 ----- .../filemanager/utils/FingerprintHandler.kt | 70 ++++ .../ic_fingerprint_grey600_36dp.png | Bin 1887 -> 0 bytes .../ic_fingerprint_grey600_36dp.png | Bin 1271 -> 0 bytes .../ic_fingerprint_grey600_36dp.png | Bin 2332 -> 0 bytes .../ic_fingerprint_grey600_36dp.png | Bin 3415 -> 0 bytes .../ic_fingerprint_grey600_36dp.png | Bin 4478 -> 0 bytes ...log_decrypt_fingerprint_authentication.xml | 31 -- .../dialogs/DecryptFingerprintDialogTest.kt | 7 +- gradle/libs.versions.toml | 2 + 15 files changed, 412 insertions(+), 417 deletions(-) delete mode 100644 app/src/main/java/com/amaze/filemanager/filesystem/files/EncryptDecryptUtils.java create mode 100644 app/src/main/java/com/amaze/filemanager/filesystem/files/EncryptDecryptUtils.kt delete mode 100644 app/src/main/java/com/amaze/filemanager/utils/FingerprintHandler.java create mode 100644 app/src/main/java/com/amaze/filemanager/utils/FingerprintHandler.kt delete mode 100644 app/src/main/res/drawable-hdpi/ic_fingerprint_grey600_36dp.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_fingerprint_grey600_36dp.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_fingerprint_grey600_36dp.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_fingerprint_grey600_36dp.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_fingerprint_grey600_36dp.png delete mode 100644 app/src/main/res/layout/dialog_decrypt_fingerprint_authentication.xml diff --git a/app/build.gradle b/app/build.gradle index b7d0ce2d23..5685923d8f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -145,6 +145,7 @@ dependencies { implementation libs.androidX.cardview implementation libs.androidX.constraintLayout implementation libs.androidX.multidex //Multiple dex files + implementation libs.androidX.biometric implementation libs.room.runtime implementation libs.room.rxjava2 diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/EncryptDecryptUtils.java b/app/src/main/java/com/amaze/filemanager/filesystem/files/EncryptDecryptUtils.java deleted file mode 100644 index 66ce02a424..0000000000 --- a/app/src/main/java/com/amaze/filemanager/filesystem/files/EncryptDecryptUtils.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2014-2024 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.amaze.filemanager.filesystem.files; - -import static com.amaze.filemanager.asynchronous.services.EncryptService.TAG_AESCRYPT; -import static com.amaze.filemanager.filesystem.files.CryptUtil.AESCRYPT_EXTENSION; - -import java.io.IOException; -import java.security.GeneralSecurityException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.amaze.filemanager.R; -import com.amaze.filemanager.asynchronous.management.ServiceWatcherUtil; -import com.amaze.filemanager.asynchronous.services.DecryptService; -import com.amaze.filemanager.asynchronous.services.EncryptService; -import com.amaze.filemanager.database.CryptHandler; -import com.amaze.filemanager.database.models.explorer.EncryptedEntry; -import com.amaze.filemanager.fileoperations.filesystem.OpenMode; -import com.amaze.filemanager.filesystem.HybridFileParcelable; -import com.amaze.filemanager.ui.activities.MainActivity; -import com.amaze.filemanager.ui.dialogs.DecryptFingerprintDialog; -import com.amaze.filemanager.ui.dialogs.GeneralDialogCreation; -import com.amaze.filemanager.ui.fragments.MainFragment; -import com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants; -import com.amaze.filemanager.ui.provider.UtilitiesProvider; -import com.amaze.filemanager.utils.PasswordUtil; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Build; -import android.util.Base64; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.appcompat.widget.AppCompatEditText; -import androidx.preference.PreferenceManager; - -/** - * Provides useful interfaces and methods for encryption/decryption - * - * @author Emmanuel on 25/5/2017, at 16:55. - */ -public class EncryptDecryptUtils { - - public static final String DECRYPT_BROADCAST = "decrypt_broadcast"; - private static final Logger LOG = LoggerFactory.getLogger(EncryptDecryptUtils.class); - - /** - * Queries database to map path and password. Starts the encryption process after database query - * - * @param path the path of file to encrypt - * @param password the password in plaintext - * @throws GeneralSecurityException Errors on encrypting file/folder - * @throws IOException I/O errors on encrypting file/folder - */ - public static void startEncryption( - Context c, final String path, final String password, Intent intent) - throws GeneralSecurityException, IOException { - String destPath = - path.substring(0, path.lastIndexOf('/') + 1) - .concat(intent.getStringExtra(EncryptService.TAG_ENCRYPT_TARGET)); - - // EncryptService.TAG_ENCRYPT_TARGET already has the .aze extension, no need to append again - if (!intent.getBooleanExtra(TAG_AESCRYPT, false)) { - EncryptedEntry encryptedEntry = new EncryptedEntry(destPath, password); - CryptHandler.INSTANCE.addEntry(encryptedEntry); - } - // start the encryption process - ServiceWatcherUtil.runService(c, intent); - } - - public static void decryptFile( - Context c, - final MainActivity mainActivity, - final MainFragment main, - OpenMode openMode, - HybridFileParcelable sourceFile, - String decryptPath, - UtilitiesProvider utilsProvider, - boolean broadcastResult) { - - Intent decryptIntent = new Intent(main.getContext(), DecryptService.class); - decryptIntent.putExtra(EncryptService.TAG_OPEN_MODE, openMode.ordinal()); - decryptIntent.putExtra(EncryptService.TAG_SOURCE, sourceFile); - decryptIntent.putExtra(EncryptService.TAG_DECRYPT_PATH, decryptPath); - SharedPreferences preferences1 = - PreferenceManager.getDefaultSharedPreferences(main.getContext()); - - if (sourceFile.getPath().endsWith(AESCRYPT_EXTENSION)) { - GeneralDialogCreation.showPasswordDialog( - c, - mainActivity, - utilsProvider.getAppTheme(), - R.string.crypt_decrypt, - R.string.authenticate_password, - (dialog, which) -> { - AppCompatEditText editText = dialog.getView().findViewById(R.id.singleedittext_input); - decryptIntent.putExtra(EncryptService.TAG_PASSWORD, editText.getText().toString()); - ServiceWatcherUtil.runService(c, decryptIntent); - dialog.dismiss(); - }, - null); - } else { - EncryptedEntry encryptedEntry; - - try { - encryptedEntry = findEncryptedEntry(sourceFile.getPath()); - } catch (GeneralSecurityException | IOException e) { - LOG.warn("failed to find encrypted entry while decrypting", e); - - // we couldn't find any entry in database or lost the key to decipher - Toast.makeText( - main.getContext(), - main.getActivity().getString(R.string.crypt_decryption_fail), - Toast.LENGTH_LONG) - .show(); - return; - } - - DecryptButtonCallbackInterface decryptButtonCallbackInterface = - new DecryptButtonCallbackInterface() { - @Override - public void confirm(Intent intent) { - ServiceWatcherUtil.runService(c, intent); - } - - @Override - public void failed() { - Toast.makeText( - main.getContext(), - main.getActivity().getString(R.string.crypt_decryption_fail_password), - Toast.LENGTH_LONG) - .show(); - } - }; - - if (encryptedEntry == null && !sourceFile.getPath().endsWith(AESCRYPT_EXTENSION)) { - // couldn't find the matching path in database, we lost the password - - Toast.makeText( - main.getContext(), - main.getActivity().getString(R.string.crypt_decryption_fail), - Toast.LENGTH_LONG) - .show(); - return; - } - - switch (encryptedEntry.getPassword().value) { - case PreferencesConstants.ENCRYPT_PASSWORD_FINGERPRINT: - try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - DecryptFingerprintDialog.show( - c, - mainActivity, - decryptIntent, - utilsProvider.getAppTheme(), - decryptButtonCallbackInterface); - } else throw new IllegalStateException("API < M!"); - } catch (GeneralSecurityException | IOException | IllegalStateException e) { - LOG.warn("failed to form fingerprint dialog", e); - Toast.makeText( - main.getContext(), - main.getString(R.string.crypt_decryption_fail), - Toast.LENGTH_LONG) - .show(); - } - break; - case PreferencesConstants.ENCRYPT_PASSWORD_MASTER: - try { - GeneralDialogCreation.showDecryptDialog( - c, - mainActivity, - decryptIntent, - utilsProvider.getAppTheme(), - PasswordUtil.INSTANCE.decryptPassword( - c, - preferences1.getString( - PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD, - PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD_DEFAULT), - Base64.DEFAULT), - decryptButtonCallbackInterface); - } catch (GeneralSecurityException | IOException e) { - LOG.warn("failed to show decrypt dialog, e"); - Toast.makeText( - main.getContext(), - main.getString(R.string.crypt_decryption_fail), - Toast.LENGTH_LONG) - .show(); - } - break; - default: - GeneralDialogCreation.showDecryptDialog( - c, - mainActivity, - decryptIntent, - utilsProvider.getAppTheme(), - encryptedEntry.getPassword().value, - decryptButtonCallbackInterface); - break; - } - } - } - - /** - * Queries database to find entry for the specific path - * - * @param path the path to match with - * @return the entry - */ - private static EncryptedEntry findEncryptedEntry(String path) - throws GeneralSecurityException, IOException { - - CryptHandler handler = CryptHandler.INSTANCE; - - EncryptedEntry matchedEntry = null; - // find closest path which matches with database entry - for (EncryptedEntry encryptedEntry : handler.getAllEntries()) { - if (path.contains(encryptedEntry.getPath())) { - - if (matchedEntry == null - || matchedEntry.getPath().length() < encryptedEntry.getPath().length()) { - matchedEntry = encryptedEntry; - } - } - } - return matchedEntry; - } - - public interface EncryptButtonCallbackInterface { - /** - * Callback fired when user has entered a password for encryption Not called when we've a master - * password set or enable fingerprint authentication - * - * @param password the password entered by user - */ - default void onButtonPressed(@NonNull Intent intent, @NonNull String password) - throws GeneralSecurityException, IOException {} - } - - public interface DecryptButtonCallbackInterface { - /** Callback fired when we've confirmed the password matches the database */ - default void confirm(@NonNull Intent intent) {} - - /** Callback fired when password doesn't match the value entered by user */ - default void failed() {} - } -} diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/EncryptDecryptUtils.kt b/app/src/main/java/com/amaze/filemanager/filesystem/files/EncryptDecryptUtils.kt new file mode 100644 index 0000000000..a389000915 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/EncryptDecryptUtils.kt @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2014-2024 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.amaze.filemanager.filesystem.files + +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.os.Build +import android.util.Base64 +import android.widget.Toast +import androidx.appcompat.widget.AppCompatEditText +import androidx.preference.PreferenceManager +import com.afollestad.materialdialogs.DialogAction +import com.afollestad.materialdialogs.MaterialDialog +import com.amaze.filemanager.R +import com.amaze.filemanager.asynchronous.management.ServiceWatcherUtil +import com.amaze.filemanager.asynchronous.services.DecryptService +import com.amaze.filemanager.asynchronous.services.EncryptService +import com.amaze.filemanager.database.CryptHandler +import com.amaze.filemanager.database.CryptHandler.addEntry +import com.amaze.filemanager.database.models.explorer.EncryptedEntry +import com.amaze.filemanager.fileoperations.filesystem.OpenMode +import com.amaze.filemanager.filesystem.HybridFileParcelable +import com.amaze.filemanager.ui.activities.MainActivity +import com.amaze.filemanager.ui.dialogs.DecryptFingerprintDialog.show +import com.amaze.filemanager.ui.dialogs.GeneralDialogCreation +import com.amaze.filemanager.ui.fragments.MainFragment +import com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants +import com.amaze.filemanager.ui.provider.UtilitiesProvider +import com.amaze.filemanager.utils.PasswordUtil.decryptPassword +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.io.IOException +import java.security.GeneralSecurityException + +/** + * Provides useful interfaces and methods for encryption/decryption + * + * @author Emmanuel on 25/5/2017, at 16:55. + */ +object EncryptDecryptUtils { + const val DECRYPT_BROADCAST: String = "decrypt_broadcast" + + private val LOG: Logger = LoggerFactory.getLogger(EncryptDecryptUtils::class.java) + + /** + * Queries database to map path and password. Starts the encryption process after database query + * + * @param path the path of file to encrypt + * @param password the password in plaintext + * @throws GeneralSecurityException Errors on encrypting file/folder + * @throws IOException I/O errors on encrypting file/folder + */ + @JvmStatic + @Throws(GeneralSecurityException::class, IOException::class) + fun startEncryption( + c: Context?, + path: String, + password: String?, + intent: Intent, + ) { + val destPath = + path.substring( + 0, + path.lastIndexOf('/') + 1, + ) + intent.getStringExtra(EncryptService.TAG_ENCRYPT_TARGET) + + // EncryptService.TAG_ENCRYPT_TARGET already has the .aze extension, no need to append again + if (!intent.getBooleanExtra(EncryptService.TAG_AESCRYPT, false)) { + val encryptedEntry = EncryptedEntry(destPath, password) + addEntry(encryptedEntry) + } + // start the encryption process + ServiceWatcherUtil.runService(c, intent) + } + + /** + * Routine to decrypt file. Include branches for AESCrypt, password and fingerprint methods. + */ + @JvmStatic + fun decryptFile( + c: Context, + mainActivity: MainActivity, + main: MainFragment, + openMode: OpenMode, + sourceFile: HybridFileParcelable, + decryptPath: String?, + utilsProvider: UtilitiesProvider, + broadcastResult: Boolean, + ) { + val decryptIntent = Intent(main.context, DecryptService::class.java) + decryptIntent.putExtra(EncryptService.TAG_OPEN_MODE, openMode.ordinal) + decryptIntent.putExtra(EncryptService.TAG_SOURCE, sourceFile) + decryptIntent.putExtra(EncryptService.TAG_DECRYPT_PATH, decryptPath) + val preferences = PreferenceManager.getDefaultSharedPreferences(main.requireContext()) + + if (sourceFile.path.endsWith(CryptUtil.AESCRYPT_EXTENSION)) { + displayDecryptDialogForAescrypt(c, mainActivity, utilsProvider, decryptIntent, main) + } else { + val encryptedEntry: EncryptedEntry? + + try { + encryptedEntry = findEncryptedEntry(sourceFile.path) + } catch (e: GeneralSecurityException) { + LOG.warn("failed to find encrypted entry while decrypting", e) + // we couldn't find any entry in database or lost the key to decipher + toastDecryptionFailure(main) + return + } catch (e: IOException) { + LOG.warn("failed to find encrypted entry while decrypting", e) + toastDecryptionFailure(main) + return + } + + val decryptButtonCallbackInterface: DecryptButtonCallbackInterface = createCallback(main) + + if (encryptedEntry == null && !sourceFile.path.endsWith(CryptUtil.AESCRYPT_EXTENSION)) { + // couldn't find the matching path in database, we lost the password + toastDecryptionFailure(main) + return + } + + when (encryptedEntry!!.password.value) { + PreferencesConstants.ENCRYPT_PASSWORD_FINGERPRINT -> + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + show( + c, + mainActivity, + decryptIntent, + decryptButtonCallbackInterface, + ) + } else { + throw IllegalStateException("API < M!") + } + } catch (e: GeneralSecurityException) { + LOG.warn("failed to form fingerprint dialog", e) + toastDecryptionFailure(main) + } catch (e: IOException) { + LOG.warn("failed to form fingerprint dialog", e) + toastDecryptionFailure(main) + } catch (e: IllegalStateException) { + LOG.warn("failed to form fingerprint dialog", e) + toastDecryptionFailure(main) + } + + PreferencesConstants.ENCRYPT_PASSWORD_MASTER -> + try { + displayDecryptDialogWithMasterPassword( + c, + mainActivity, + decryptIntent, + utilsProvider, + preferences, + decryptButtonCallbackInterface, + ) + } catch (e: GeneralSecurityException) { + LOG.warn("failed to show decrypt dialog, e") + toastDecryptionFailure(main) + } catch (e: IOException) { + LOG.warn("failed to show decrypt dialog, e") + toastDecryptionFailure(main) + } + + else -> + GeneralDialogCreation.showDecryptDialog( + c, + mainActivity, + decryptIntent, + utilsProvider.appTheme, + encryptedEntry.password.value, + decryptButtonCallbackInterface, + ) + } + } + } + + private fun displayDecryptDialogWithMasterPassword( + c: Context, + mainActivity: MainActivity, + decryptIntent: Intent, + utilsProvider: UtilitiesProvider, + preferences: SharedPreferences, + decryptButtonCallbackInterface: DecryptButtonCallbackInterface, + ) { + GeneralDialogCreation.showDecryptDialog( + c, + mainActivity, + decryptIntent, + utilsProvider.appTheme, + decryptPassword( + c, + preferences.getString( + PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD, + PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD_DEFAULT, + )!!, + Base64.DEFAULT, + ), + decryptButtonCallbackInterface, + ) + } + + private fun displayDecryptDialogForAescrypt( + c: Context?, + mainActivity: MainActivity?, + utilsProvider: UtilitiesProvider, + decryptIntent: Intent, + main: MainFragment, + ) { + GeneralDialogCreation.showPasswordDialog( + c!!, + mainActivity!!, + utilsProvider.appTheme, + R.string.crypt_decrypt, + R.string.authenticate_password, + { dialog: MaterialDialog, which: DialogAction? -> + val editText = + dialog.view.findViewById(R.id.singleedittext_input) + decryptIntent.putExtra(EncryptService.TAG_PASSWORD, editText.text.toString()) + ServiceWatcherUtil.runService(main.context, decryptIntent) + dialog.dismiss() + }, + null, + ) + } + + private fun createCallback(main: MainFragment): DecryptButtonCallbackInterface { + return object : DecryptButtonCallbackInterface { + override fun confirm(intent: Intent) { + ServiceWatcherUtil.runService(main.context, intent) + } + + override fun failed() { + Toast.makeText( + main.context, + main.requireMainActivity().getString(R.string.crypt_decryption_fail_password), + Toast.LENGTH_LONG, + ).show() + } + } + } + + private fun toastDecryptionFailure(main: MainFragment) { + Toast.makeText( + main.context, + main.requireMainActivity().getString(R.string.crypt_decryption_fail), + Toast.LENGTH_LONG, + ).show() + } + + /** + * Queries database to find entry for the specific path + * + * @param path the path to match with + * @return the entry + */ + @JvmStatic + @Throws(GeneralSecurityException::class, IOException::class) + private fun findEncryptedEntry(path: String): EncryptedEntry? { + val handler = CryptHandler + + var matchedEntry: EncryptedEntry? = null + // find closest path which matches with database entry + for (encryptedEntry in handler.allEntries) { + if (path.contains(encryptedEntry.path)) { + if (matchedEntry == null || + matchedEntry.path.length < encryptedEntry.path.length + ) { + matchedEntry = encryptedEntry + } + } + } + return matchedEntry + } + + interface EncryptButtonCallbackInterface { + /** + * Callback fired when user has entered a password for encryption Not called when we've a master + * password set or enable fingerprint authentication + * + * @param password the password entered by user + */ + @Throws(GeneralSecurityException::class, IOException::class) + fun onButtonPressed( + intent: Intent, + password: String, + ) { + } + } + + interface DecryptButtonCallbackInterface { + /** Callback fired when we've confirmed the password matches the database */ + fun confirm(intent: Intent) {} + + /** Callback fired when password doesn't match the value entered by user */ + fun failed() {} + } +} diff --git a/app/src/main/java/com/amaze/filemanager/ui/dialogs/DecryptFingerprintDialog.kt b/app/src/main/java/com/amaze/filemanager/ui/dialogs/DecryptFingerprintDialog.kt index f73cfec0ee..0fb2355abb 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/dialogs/DecryptFingerprintDialog.kt +++ b/app/src/main/java/com/amaze/filemanager/ui/dialogs/DecryptFingerprintDialog.kt @@ -22,17 +22,17 @@ package com.amaze.filemanager.ui.dialogs import android.content.Context import android.content.Intent -import android.hardware.fingerprint.FingerprintManager import android.os.Build -import android.view.View import androidx.annotation.RequiresApi -import androidx.appcompat.widget.AppCompatButton -import com.afollestad.materialdialogs.MaterialDialog +import androidx.biometric.BiometricManager +import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG +import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK +import androidx.biometric.BiometricManager.BIOMETRIC_SUCCESS +import androidx.biometric.BiometricPrompt import com.amaze.filemanager.R import com.amaze.filemanager.filesystem.files.CryptUtil import com.amaze.filemanager.filesystem.files.EncryptDecryptUtils.DecryptButtonCallbackInterface import com.amaze.filemanager.ui.activities.MainActivity -import com.amaze.filemanager.ui.theme.AppTheme import com.amaze.filemanager.utils.FingerprintHandler import java.io.IOException import java.security.GeneralSecurityException @@ -54,26 +54,21 @@ object DecryptFingerprintDialog { c: Context, main: MainActivity, intent: Intent, - appTheme: AppTheme, decryptButtonCallbackInterface: DecryptButtonCallbackInterface, ) { - val accentColor = main.accent - val builder = MaterialDialog.Builder(c) - builder.title(c.getString(R.string.crypt_decrypt)) - val rootView = View.inflate(c, R.layout.dialog_decrypt_fingerprint_authentication, null) - val cancelButton = - rootView.findViewById( - R.id.button_decrypt_fingerprint_cancel, - ) - cancelButton.setTextColor(accentColor) - builder.customView(rootView, true) - builder.canceledOnTouchOutside(false) - builder.theme(appTheme.getMaterialDialogTheme()) - val dialog = builder.show() - cancelButton.setOnClickListener { v: View? -> dialog.cancel() } - val manager = c.getSystemService(FingerprintManager::class.java) - val handler = FingerprintHandler(c, intent, dialog, decryptButtonCallbackInterface) - val `object` = FingerprintManager.CryptoObject(CryptUtil.initCipher()) - handler.authenticate(manager, `object`) + val manager = BiometricManager.from(c) + if (manager.canAuthenticate(BIOMETRIC_STRONG or BIOMETRIC_WEAK) == BIOMETRIC_SUCCESS) { + val promptInfo = + BiometricPrompt.PromptInfo.Builder() + .setTitle(c.getString(R.string.crypt_decrypt)) + .setDescription(c.getString(R.string.crypt_fingerprint_authenticate)) + .setConfirmationRequired(false) + .setNegativeButtonText(c.getString(android.R.string.cancel)) + .build() + + val handler = FingerprintHandler(main, intent, promptInfo, decryptButtonCallbackInterface) + val `object` = BiometricPrompt.CryptoObject(CryptUtil.initCipher()) + handler.authenticate(`object`) + } } } diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java b/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java index 29c2a3393b..f27bf51aa3 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java @@ -494,8 +494,8 @@ public void onListItemClicked( .replace(CryptUtil.AESCRYPT_EXTENSION, "")); EncryptDecryptUtils.decryptFile( - getContext(), - getMainActivity(), + requireContext(), + requireMainActivity(), this, mainFragmentViewModel.getOpenMode(), layoutElementParcelable.generateBaseFile(), diff --git a/app/src/main/java/com/amaze/filemanager/utils/FingerprintHandler.java b/app/src/main/java/com/amaze/filemanager/utils/FingerprintHandler.java deleted file mode 100644 index 4e7e6d580b..0000000000 --- a/app/src/main/java/com/amaze/filemanager/utils/FingerprintHandler.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2014-2024 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.amaze.filemanager.utils; - -import com.afollestad.materialdialogs.MaterialDialog; -import com.amaze.filemanager.filesystem.files.EncryptDecryptUtils; - -import android.Manifest; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.hardware.fingerprint.FingerprintManager; -import android.os.Build; -import android.os.CancellationSignal; - -import androidx.annotation.RequiresApi; -import androidx.core.app.ActivityCompat; - -/** Created by vishal on 15/4/17. */ -@RequiresApi(api = Build.VERSION_CODES.M) -public class FingerprintHandler extends FingerprintManager.AuthenticationCallback { - - private Context context; - private EncryptDecryptUtils.DecryptButtonCallbackInterface decryptButtonCallbackInterface; - private Intent decryptIntent; - private MaterialDialog materialDialog; - - // Constructor - public FingerprintHandler( - Context mContext, - Intent intent, - MaterialDialog materialDialog, - EncryptDecryptUtils.DecryptButtonCallbackInterface decryptButtonCallbackInterface) { - context = mContext; - this.decryptIntent = intent; - this.materialDialog = materialDialog; - this.decryptButtonCallbackInterface = decryptButtonCallbackInterface; - } - - @RequiresApi(api = Build.VERSION_CODES.M) - public void authenticate( - FingerprintManager manager, FingerprintManager.CryptoObject cryptoObject) { - - CancellationSignal cancellationSignal = new CancellationSignal(); - if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) - != PackageManager.PERMISSION_GRANTED) { - return; - } - manager.authenticate(cryptoObject, cancellationSignal, 0, this, null); - } - - @Override - public void onAuthenticationError(int errMsgId, CharSequence errString) {} - - @Override - public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {} - - @Override - public void onAuthenticationFailed() { - materialDialog.cancel(); - decryptButtonCallbackInterface.failed(); - } - - @Override - public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { - - materialDialog.cancel(); - decryptButtonCallbackInterface.confirm(decryptIntent); - } -} diff --git a/app/src/main/java/com/amaze/filemanager/utils/FingerprintHandler.kt b/app/src/main/java/com/amaze/filemanager/utils/FingerprintHandler.kt new file mode 100644 index 0000000000..45f109f483 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/utils/FingerprintHandler.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2014-2024 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.amaze.filemanager.utils + +import android.Manifest +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.biometric.BiometricPrompt +import androidx.biometric.BiometricPrompt.PromptInfo +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import com.amaze.filemanager.filesystem.files.EncryptDecryptUtils.DecryptButtonCallbackInterface +import com.amaze.filemanager.ui.activities.MainActivity + +/** Created by vishal on 15/4/17. */ +@RequiresApi(api = Build.VERSION_CODES.M) +class FingerprintHandler( + private val mainActivity: MainActivity, + private val decryptIntent: Intent, + private val promptInfo: PromptInfo, + private val decryptButtonCallbackInterface: DecryptButtonCallbackInterface, +) : BiometricPrompt.AuthenticationCallback() { + /** + * Authenticate user to perform decryption. + */ + @RequiresApi(api = Build.VERSION_CODES.M) + fun authenticate(cryptoObject: BiometricPrompt.CryptoObject) { + if (ActivityCompat.checkSelfPermission(mainActivity, Manifest.permission.USE_FINGERPRINT) + != PackageManager.PERMISSION_GRANTED + ) { + return + } + + val prompt = + BiometricPrompt(mainActivity, ContextCompat.getMainExecutor(mainActivity), this) + prompt.authenticate(promptInfo, cryptoObject) + } + + override fun onAuthenticationError( + errMsgId: Int, + errString: CharSequence, + ) = Unit + + override fun onAuthenticationFailed() { + decryptButtonCallbackInterface.failed() + } + + override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { + decryptButtonCallbackInterface.confirm(decryptIntent) + } +} diff --git a/app/src/main/res/drawable-hdpi/ic_fingerprint_grey600_36dp.png b/app/src/main/res/drawable-hdpi/ic_fingerprint_grey600_36dp.png deleted file mode 100644 index 263728840acca811ccce2540a8cdcede6f7b525c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1887 zcmZ{ldpy$%AIE={;#kZr)D|9>u|h)_7N=a=$fbod&yHiWi^s9CxyvC`w6IRU+$wRz z;kZ8|j75&gP&mjfDl<`0k6Z5X?0Nos{&@cQeBa;C_xt+%@%`(|@$$H+Ab&z0000FX z)(J1A)qf->Bdv%_kA0<(2|Dk79sqcEAiLzlQm;e7;@tt@DgpqKZUDfxbd>Y~0HRO; z@QDNfws`=c!OCyBjFB!52D)8z0)UE4J#(r1j)nD$kP`b}x%7nCOB>lpoV&B^EaW5v za^R?p+av%S!Q-6HdlQ78^AfVMeU5c$)0I?;G)v9So_ECWV;Pn?r7I({0qWv9zroy8 zI&QD4|PaJtoY&!ISx)QGr}y|5o}s9S;b)N&QD+tes{ zsrR(|HA~Ry@U7+E&EY$^s#%iq;3_1Wn2JXZ-t?=Gc!X z?Z;s+y8Ix-`$Nm`y2#}ae1kYJ-fj36H-iE1(%6F73H=hNIDcB6=3G5D;pF4U8_cTN z?NVjK%=M3V*RwsmKVVj?AY*e!)|CGcXRx3b-5L`(BEoa?9A1hx?~$HCh@c}g0s9q| zeogHk%t|I5y}C*D4EQs5+*jt8)TZ@=f3WkaxiA6*@_9|wp^u=2n^shAk%X4rJx&`i z804QO`oFArfI37uz7cV4lWQV3R{Ha#^<20WBFTY)X(Hi7xykKIK5@R8bSSreYc&j;q^?fcD4eV1V>qi$>k_o*+>xll-dayM-&DS` zH7(bb&!0N6*z3)#uu3>Sc4G_UN@PdTgnxa=T{7p^m)6wTWnMF?JC)qR!EhtS+j=Hj zuKtbK2j9zY|2*9XX0`o#2(49T$Q>22@}D`olgM>FXYfI}ejJzcf7z=A?}K* z4I(_Yyvib!=CiR7du2)xqy?Nev?JD))KUGD{Qhl}| zHzLB%D?eSbYp(9WomoteRBVEao`1{8Em@<&p`LDdw6-X&0%O?U$$m&2T6=k~WNBH3 zRBS0rcyMbdxpY+@iE!b2gX7$L4sF{}w253crXa73wIVhyuL1qhp$g7HP~HwJU;D{$ zcbg8bQB&01l7H))rU;gXQ*g9Nt@i;L7u3c)RF-u62fN7lt5iE2!<0Kr;KIfvPjL7O zb&>EKs21AE-pTU*A-HmaoQnlb{$xMY!97h$a|b&|&{|)NWEqG058&i2SW^!SIJGme zv0=$Tk8&Mai;hzfunKbZYml&vpxdP3Ns9~td+IHcR>8`@XwYMyA~6Q_cjC+vttEWR z><~XD`_Nv9ReFTgb_)vz32_hVZk>^5=>1Z{%1H7 z9aoA|iGsU^218l?+q$Vi6?G##-si!Q0rhckYI<`-FWP;1rMLR>N6J3TH1qff+vUX$ zeZD`n2w(Gz+0Irh?Q%vc7SiGk`R_J9x?wk?7`68*;MF#Pef6H4g^v(sjR{!+x@zJ3 zd+moSM=grM&PRHH ziHr&OXyd5e*<4@Z@eJ{cZSzkJIc$vuac=W%)4%5>X3z>+7e=7P`@-cg`pWb|iC6al z>BBLIbS6Yn$&oZ$N;pjlz#L_cGBGnVF}LwHGqW{A*`7t8Mxks`sN}_2;{ODgVboxH X-2V?O@qV0_1^}G1hf~c3Qp&#puzq>m diff --git a/app/src/main/res/drawable-mdpi/ic_fingerprint_grey600_36dp.png b/app/src/main/res/drawable-mdpi/ic_fingerprint_grey600_36dp.png deleted file mode 100644 index 891cff892af5841856339bbd610394dc2f3f8330..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1271 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8JTOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>a^UBDXAiD?+bIc zmz>?w6Qg_jijUJ3r?Xei_;4(lB(tzbW!susTQY^;S!4n%Y#UN9mcn^Ul6Jo)g%;neX4{%6hpS_kQ1bKIgg3CDzhMdS%`- zq*mpsoqRJRrvI};%KD8Ry?;9YKRi1x*^s?J5sE0&8M&*VC-!kr@WDXVbZJP5_G1>IYL)mSUkN-R^^``C8ntN@R zle~=%Pt91qnP2nU<2l=w-#&h8ZidbAf@{y$O`Vi_=a@|NNi(w>QgK@|7p@kW-fptI zVA}Dgu7-(9%TG=YR@yfs{m06=&gU}4qNb*pf6~pHFXHIhZ0Tng;+?4BYf)(yC8Czf z_Vm~7jOrs&7M;bX(mYD{W*n*f{U-H(Q~Pt3V}(rHP5)m}zpfl3Sw3yat$Ambi#~Tf z`Rfz+xhI<&r4GMJi8Prp`3dJFk-V*JeA`sNTJj&CyEMb5y)bL(n}}I&)+yDawNG~GOSQflkLlYN}83+m4I zO+ED3;qfGttaw-IOI06we2)4r*F!5p1Qkzx}Gv;S-;; z?>-gs-*&c!`~9_VQ{Je~5e?cQo}OEHfyu@v|MKKXPEyNfZoRUZpTD}uHb}IrA#AnR zw8EBWP3JaSZV}_Zu6a;#*4pS8$-2ds#xau~6@@)GGxgBviVBZM&T|TnI`r-knz_1g zn@1v39&b>IxuxcH`RCdvmx(s)X>4cjZv6JCUBvC-9`)8N8yo$NjQ-!JSsd$RnrE&m z_V7;pnZ>qSR8C7Y-u60~_9$_li>;iRN6wMll2uJNcQSfiXlb9_rFpBt{qLpe4^6y$ zjvZ#7WZT%z8Z@J;-L@xKX;+hL>zD3EG4W;X@)^CmBh1eg=;-->U)FlKaOn{znddxx z#Tw=dEM8nZv_G=zHjn0-!_He%ybE5bo?NzZ-rt(!^~Is>w|V&Ez026oEpuxAl)SBa z+wR)-&03|IC%>Dm_lb9hZ_Zq` z;&m!}LG&b@ZtcT1egCS%-|5EQPT#Rt&DHr0!?|Z?6f{Exd!Ku*-n^z)Pcr0oyYiDt zhol8>4`lM4&UsR~*Y%J~RE~S%Wy9HzE#%bZifdVKGuSjqWLMkqLz_z8b3YIKW*&6a zqia=}aTKs1P%UwdC`m~yNwrEYN(E93Mg~R(x`u|jMwTIlhE|3KRwfqO1_o9J290l? fM5AcP%}>cptHiD0!^W8BKn)C@u6{1-oD!M<)KfKJ diff --git a/app/src/main/res/drawable-xhdpi/ic_fingerprint_grey600_36dp.png b/app/src/main/res/drawable-xhdpi/ic_fingerprint_grey600_36dp.png deleted file mode 100644 index 95492488d881cbafdf939f2ac644a6eda3ff6e2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2332 zcmV+%3FG#OP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00002 zVoOIv0063uBQgL0010qNS#tmY07w7;07w8v$!k6U000McNliru}O3NbUrN;Uuh z2oFg_K~!ko?OJPe71b4ehuB+?ny1XltfgzYo3$cuz!&Z)Q4BKfF)=(+Ww8uET zI}r+o(0qQ0zJ%M#eHu0N5ar}|>rf~KeG$g?R)Hkmhv_l8u0z1kMf4Dkwy0@vnjP9%eb2sRYvtC8+mp(THmLB~73K&N=3jPG;o0Gz}&6CQc`SI)M9}9;x!0 zHw?jNP=Tr8th2qteP|Na;DiZ^Uw6_|04LH<38XLa2n`7GuG1T{@D~Hrkko0Y39IH` znxx6cp%N}!>i%ZnLprvq7ElfhmWuWFQ9~H+QXXZc?3`4 zJ{=No;VjHXWrCA8zdxgLo|w|uYU#4a@LHS-z(YrPNpxUGffPup9gF zpBA2I#M9BiDF#!NjX$8tw@odT(R98tkz3|p%0EIF6{zz){S_}M=$pFiH5rEt;s3&~ zA|uWk@aG^Gt9?9lAaYYG@>h3y%nmMG&a(&nYmY*Y~7CF$0gXQMZFrm!pv@TD^}uQi;>lYz4(p&Lo3Z- zvTOMR4Jo$Z;^gS?8?;Yt!HeP?O9QCvtSsch#fNOiwc+%{Qk{bKnLn=W#@elmtvmxv zd9ys;+Fi$XoE$kHjS5mxc0gCwoKsH|WHJq5fA+>{Y~$r%Or{*lp&XvU#r#a%IC8&l zk2d18IXUKPNw>ANpZL4H@`MUpen$&_EwLgh=w5g^U1yKx zq0#f31;&q!NL;2NJuWkUYg;xy7hieF!LxEfckIy7W}7u1Mqx2(%w$XZcr8LEbrxkC z^=64gTo3H=;;^j&Z=)XDRYJrvoh0)NFgLf-e;mQX^UpH^WP@hvY4Y|Uyi8S*>yu%l z8Jg~HS91$4=9k*g0Nt%j@ZiZ~jR^mfVrtxA^sKvayPr)$8OY_eDzM?=Jw<@_aLqt@$(LlqaRx42 zoCx<9?F`k;Vgujvb-&dfE=9cy7dsSN;eMIfpLE8@YEknd0+gl*)SrydCD%yqXA?j#w%o0K2%J%0AU zAmJyMCh2Fc!3Hs_cb~wj$Tu}4#mUD#lRN2nUnJxt)81|JURLEQ9u)&Vqc>d8V&<>s znfi$enOiI}=lF9#kR~`24RGONrxfq!Npv-+@`{UFVIfi_znJ|C&zO;Z!t48k8|+#U z{h*A0CgasIzEN-fG44k$1Lpj#>sQbMXz^=&R!F}e=97aNF*7u_2a(68mi0YZ2l|NKM5j#S$wa*C6wavY77H@|=j7k||` z(0g-fP!4+Ds|mdgBO_#!@`T@cmxTTW7^&(2x>dG(M1I;U-q@3*ehR-56wnqgZ|h>q z69XAP?Tx*PpzvxOgNqI7@J|4(7eFh#v6n=OvgM~#1qD=XMg!-Im}$P%Mvvf>d8L;~ zlStYw&n`9(tD=K-k`2g0RX#uxV|0sb;^pq6egfd}vid1pxcFRFA*+{JmSsh*7fI`q z19ZsGxe0)jU(Ax#IRZhdCB8D=-|-Sz`?)_qw%uf2n4gg?XN%gW%IaG(zQo5_EB*T9 z6)M%L92d&aba6w_uoqYKvJmBn4t^o4Yh}yt`$*w{Vs#lesVLN)-ES3{20o9)0|H^3 zWm#7A3Ppnns{#-&nA{Wh8@RNN#yOdmMZJ)VZF+Wk@LDKjGOE^ra`N#a`VDznya*e$ zX)1lW?cZii5Kqs+szo0a>au=_*D6V>J>?x0JH4fvZYsQ56?3XuXm-CCJ~s(KPP$P3 ze%%`Rjr6RDf7oBVaZDX9IO$Xa^?oIkMA1{lLL6&7%(Sg!l*Lq|axk(jYUI!M= z(!I#4cs*Fq7kN~U8q}hi%IVHP2Mo;sk?5pQ#?o8KNnslHCKMTL;WPL ztG&&#K1H3d(zgY2i8jqn`d&wTJFNAJeBJXO_Ep4b^rq^*sF!GMnb9Uz;ioD3*rIiG z2hPByQVo!0Gx>;O50f(2MTKf48AhAv_4#}8XKY3_$52uDt@i3@5M56m_9uFkN!|n2``w|aCLm6gt z;Zw}(Tu^EFqilUFZX)FQ{#tMZOYJe8hA7fcpYd;u&qfET{zBh#I}K8Fu-Bs|HZydK z@8t2-&h(^FRE5fxn3%_7y_}Xst^b!RoJG@-Yjs^+*Z<)9FJnbz7_-&)cmMzZC3Hnt zbYx+4WjbSWWnpw>05UK#FfB1LEiyS&F)=zZFgi3iD=;uRFff7appnGB7PMF)cDVR539+F)%tbI4dwPIxsNsv0Uc>0000 ChDxvi diff --git a/app/src/main/res/drawable-xxhdpi/ic_fingerprint_grey600_36dp.png b/app/src/main/res/drawable-xxhdpi/ic_fingerprint_grey600_36dp.png deleted file mode 100644 index 41c72da2f4a4dc5eb7c20bfb5538e45e61fcccf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3415 zcmV-d4XE;oP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00002 zVoOIv0063uBQgL0010qNS#tmY07w7;07w8v$!k6U000McNliru}O3OGs{@{#}m z3`$8vK~#9!?Ob_$6h#&eM*;$}3yNF{3J3@*3Q<@*xMY$0(kvjCiq4`E%%tD z2-+X|0UzQeOr&8}VxaP`fFRW2p~%AFa8e>Xr?8eXsC}SfsU#`&GB9=_uRtW}&-e5) z4Jbuy8c5)w*od=5AW1%7BNI2rl`CPQ{52?pKX-{FDT90QfcJuuI;mSL%w6RtFx)9?Ia^SdAl5$omrMDZej%H8IF>VaKqH$M~-H#794G-%Vhw z#z{xwzQa^+&Eh0$oN*an;7N2$s!-6;k}Gj%Ova}U#O>oti-|105U0YX-%i7dsL{$< z9gvA^18MV$i?Bgm&VLYbz%yCof zMi7VC6V2lIYw&f{4@oqNJ?x_tzKaG>m(kiWoE8da=ny#VIAJom-Y2o@>&lY`|n zxWxPohjU7%y{`F+6H%u$oKzLcz&9@Yif4GC|MC)LKinP31*W}NNpmQL`%+ijXeIhu zrnaU|)DI&VSU{WcRYb;|Q39S~TwGinwc;HPi~29mq5*VCK1_W*3_&LU-(j5N(ankL zF&G}fkA|h};R%t{x3hV`BiLgkZk@kT@txHbcsFbTd01`rDBAw|Zo)DHLUICk7LTXc z-w=hZpjIx0u8lAcIbq{x1**i_OH2>LHQu96QKIZWSUr%$BiX|vk%Vr(mV9BvUo@eY z!)Do?dRfef^2GB(JHo`4IsQt!2)FSptaWs%j$<{Z(f}vNj5^^H+b@n-iLp``%WYF_ z^*1r>_vP&49r*@#e&S8=rqnfANX)dI+X?RMI$7Qe@K2kSnqv?ClPcggY%CN6my4Cu zD_r$ZFC4dFklkH|scolqhKJhrw<3*l4DR#$ywAe}fz4W;rY>%(jJew2-=JgI z&VKBUJHuA2EWaK{`L2;~(`)nd5te!ctFVB^^Jw;95PSH38jl57 zg`*LE^ABC6Bvn9`_WrOd@D7qRsX3;57TnpqMB0ldxPMwL=W)|(^FYkSej{lIZRb`F z3v{L}vRy-C4L3ST?KHA{22bHQrl+>fKQ=|}X%4;%S0|JbQOdK5s^TSLl*@g!spf_) z=K$SXm~cM1Iw(9&pgGb;2C872_I`wGz$lH?$XaGcZ7C&?P!!0V9}B}PmrByoYH8tY zcA>!pPrMdFzM_%!ciR$Hay@5&)oN~a;E_BWN!%G%I=jlLP4SNH)Si{3rMAWyp+Sch z7QC!3#aRMoG)I>QwoHpntHmCA#t>~~V;xOHPZM$+sE8Rh(w@?I=mGJr@LoT*hT_?7 z;si!(Q=gQQ7g7lWu)-Lz$>&>SnxX>SgP*kT?vo?)8|QkD79qk;y$tJ-gI&H`#c8$I z!itl=o5CESKJ=dgnB+IYom=j&&5>hu(3HqHtU;#L(Cvi8KJBz@ZmG9ERu|ac$m98G z7lm0*X-E#m9RJV)mpmld-rgP&`;GJ*dyTnv$HBY=8riU%{6CP~_Ka(r*?Rd?g zTo!94IYFCmf6S149xgq6JvgLE_BvMw6Lby0?Joj0rX%;X#dQrGbAm z7KHs_C_~4&Xqsd?1rG$uYvD>87s;}ki@lsJ!+T8~ zchlV9Dfs~6)>{_~HBwn@UyvRad;!rv$xTKaD+U(u7iCCi)^YbEJ$ocBLhU6Cr0k%# zzR;U{+*BP3hx$fM7bq{G7zw}NzHr1Bhp`L$4c^8!3Cv)F*@?nis^7(ICk}?r(yuSEgY31-y@W5(41P zVg#=k`8x%^iQebb*Dhr@X^}#D#YAM1g+LW$midLy2vu?DT<=>dN=~mWMwn&va@L66 zdHLmp{GZq>CQnTYxnd1}6S3F{CT}mbsfY`dl)hxLS+rSf@3X>^HF1Zq6(+*4#KfS~ zPDn^7tw48}9WQ(q>U~S}ZZr{wSJfD%)NWx2TM~t^Y%TmSgfQqR$Q>oV4y76RbJ4rh zWU=s}%BQ+?T||&@FdAW+Asr)pJw@(Rqx1&3nb?M39IungxOi&{w%z5HL-n-s6piWI z(;7f$^it@D(24y^d_BY*_H$dFjhR++(Q{Y+tYg8nqClXO9m5kQ<$=axsZR?MwkRh{ zB_q5wMckb2ti%He68Vt8MA1&{L7|TtnL-&{D_o^efWe!@7&9CKAHj_Dwga|R4w>?5 zNc1}xg}|rOqBM|YkofwLIvXYAT6jNQ!%`$h|E`iW;d?IDVgavv(b#pStfqu_l*Q7l zUXZU-(Xo;;9MzzjiFyGhEut>x89ucsr`h0zeJ;Q%p@on(e30vfa;FhWpzTR3Hwa4?OS&aQSSOhTH(IzL7C5AMzxOqe zGkD@L*-(I~o_F+mAh*DQ4uY_NkPemR`(B--?*>_~mR?(Ye^4rSd+~(TkQW-SS#6~| z?8Rd)wLoRDn_I=%$U>kY;%s)xG88QVtJO?}*BS&Wqc@GmOIX93@H*zws2CSJi53&CHmu#DcM0d_PoM{@ns3XkxpqbOKzW*SbZ(en`U{1$s~Ih0@b|y-wpmI{u)+8G`140 zM7kozge6x$cR%7Qy&BPKs1UPGftW;mei zS#l*?q4ILA*r6hgHCipxGqpsRlrGU^Mg%3k1~x^pLf`w8x>y|1-%xtBgmk8*)zEj; zKv8|++w9k`PzMhQw91_QoKr%CdE&**=pYUxSJ%Q+y>9rk@VhX9iqwgwVT+VjnQB*V zS@G-4o8|JQOB@%+ciED*xp>rUxjgaQ3_WQSgC@}wehh?rs8b35d5M-SvsHicNV_D_ zUKQw+@)k(YbYPBN$WC1i8gPyM#ziS@R&%Ys=7@fu!PgT1Gsw>t3@KH2pB>oozDV~O z0#!5^P5vE8mV$jsD{0zQ4ax{J41JBxR1kTx7N>Md+9t8w*TTQ>UceI!e{>SW8P^NI z+->qcgM{BSd24{b8i;FL%GuH1=v;aF+DLbX=n|TQFQow}rT-;nGqi1e*F?V+?TM#e zSc+2!;$wyX|7KcprBd;K5M`~bm9?_=8*V=XgvVZ0SgsH|0000bbVXQnWMOn=I%9HW zVRU5xGB7eQEio}IGC5Q+F*-3YIy5*dFfckWFoEpkTL1t6C3HntbYx+4WjbwdWNBu3 t05UK#FfB1LEiyS&F)=zZFgi3iD=;uRFfj13T;~7)002ovPDHLkV1giNP&NPn diff --git a/app/src/main/res/drawable-xxxhdpi/ic_fingerprint_grey600_36dp.png b/app/src/main/res/drawable-xxxhdpi/ic_fingerprint_grey600_36dp.png deleted file mode 100644 index e9fc09daf911e2e9b4bba5e4456449db50655f87..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4478 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

ejc;QUr-ZOY0BRoohE($z>@|IGcO&Y`+ zAR@k%qy1&`Fo9Vb1BK(Odr+2dUT{A1)38b4m|YDQj3XVe==e6>#CI(!+(YxX8G?$6 zP@a^2Tgv2>yPK$wO`J-@@xf8&g-JuJXoll9TuI3?!?#X6RQi@op8P)H`nvaa(RP-J zwY|3@p)XU7JvoQovN58Ow%Pb$8B=>P@%uDq21j)L5{ZlD(VQp7;LBm*DP-9p-biQL z;+>0d(pHak&6v0u1_8;`rs?x?5!mH{YCS`U>f43AgV(t+YCUU!A+n7Q2vNG_+Q~xn ztC7+!afZ<4!{i@3ert2xc0%;Tvu(TeH&{{fMe8q>w6H{fMF=6nJ<(1Wf4r$1xu4x3 zB0_GD&h1+bEHWz+k1l`voJ?^`Hok5(NOFM9oGy3mAo>97?DF!lUd#zXbJtoW#m%`h} zgOF}TY@rheRj2<0&Cz?nb}SXpxLBR?;W5GKuVU`+VDIlJ1@m=$5P*-Kzu~0WYUjZK&`{A;Zcwy~{tux)d5{1A diff --git a/app/src/main/res/layout/dialog_decrypt_fingerprint_authentication.xml b/app/src/main/res/layout/dialog_decrypt_fingerprint_authentication.xml deleted file mode 100644 index 8a7f2a11ae..0000000000 --- a/app/src/main/res/layout/dialog_decrypt_fingerprint_authentication.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/test/java/com/amaze/filemanager/ui/dialogs/DecryptFingerprintDialogTest.kt b/app/src/test/java/com/amaze/filemanager/ui/dialogs/DecryptFingerprintDialogTest.kt index 838aa2ff65..a3d20c6c30 100644 --- a/app/src/test/java/com/amaze/filemanager/ui/dialogs/DecryptFingerprintDialogTest.kt +++ b/app/src/test/java/com/amaze/filemanager/ui/dialogs/DecryptFingerprintDialogTest.kt @@ -26,7 +26,7 @@ import android.hardware.fingerprint.FingerprintManager import android.os.Build.VERSION_CODES.M import android.os.Build.VERSION_CODES.P import android.os.Environment -import androidx.annotation.RequiresApi +import androidx.test.filters.SdkSuppress import com.afollestad.materialdialogs.MaterialDialog import com.amaze.filemanager.application.AppConfig import com.amaze.filemanager.asynchronous.services.DecryptService.TAG_DECRYPT_PATH @@ -86,7 +86,7 @@ class DecryptFingerprintDialogTest : AbstractEncryptDialogTests() { * Test fingerprint authentication success scenario. */ @Test - @RequiresApi(M) + @SdkSuppress(minSdkVersion = M) fun testDecryptFingerprintDialogSuccess() { performTest( testContent = { @@ -111,7 +111,7 @@ class DecryptFingerprintDialogTest : AbstractEncryptDialogTests() { * Test fingerprint authentication failure scenario. */ @Test - @RequiresApi(M) + @SdkSuppress(minSdkVersion = M) fun testDecryptFingerprintDialogFailed() { performTest( testContent = { @@ -147,7 +147,6 @@ class DecryptFingerprintDialogTest : AbstractEncryptDialogTests() { TAG_DECRYPT_PATH, Environment.getExternalStorageDirectory().absolutePath, ), - activity.appTheme, callback, ) ShadowDialog.getLatestDialog()?.run { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bc8b19e858..63db3a42e1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -25,6 +25,7 @@ androidXPref = "1.2.1" androidXPalette = "1.0.0" androidXCardView = "1.0.0" androidXConstraintLayout = "1.1.3" +androidXBiometric = "1.1.0" androidXTest = "1.5.0" androidXTestRunner = "1.5.2" androidXTestExt = "1.1.5" @@ -84,6 +85,7 @@ androidX-multidex = { module = "androidx.multidex:multidex", version.ref = "andr androidX-palette = { module = "androidx.palette:palette-ktx", version.ref = "androidXPalette" } androidX-preference = { module = "androidx.preference:preference-ktx", version.ref = "androidXPref" } androidX-vectordrawable-animated = { module = "androidx.vectordrawable:vectordrawable-animated", version.ref = "vectordrawableAnimated" } +androidX-biometric = { module = "androidx.biometric:biometric", version.ref = "androidXBiometric" } androidX-test-core = { module = "androidx.test:core", version.ref = "androidXTest" } androidX-test-runner = { module = "androidx.test:runner", version.ref = "androidXTestRunner" }