diff --git a/app/build.gradle b/app/build.gradle index 5bba05967..71c3c634c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,7 +25,8 @@ android { } dependencies { - implementation 'com.android.support:design:28.0.0' + implementation 'com.google.android.material:material:1.0.0' + implementation 'androidx.biometric:biometric:1.0.0-alpha04' } task versionName { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d06c059ab..029fbbd09 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,7 +22,7 @@ - + @@ -48,7 +48,7 @@ tools:ignore="GoogleAppIndexingWarning"> - diff --git a/app/src/main/java/com/termux/api/DialogActivity.java b/app/src/main/java/com/termux/api/DialogActivity.java index a07df4e26..2cabc1562 100644 --- a/app/src/main/java/com/termux/api/DialogActivity.java +++ b/app/src/main/java/com/termux/api/DialogActivity.java @@ -14,11 +14,11 @@ import android.speech.RecognitionListener; import android.speech.RecognizerIntent; import android.speech.SpeechRecognizer; -import android.support.annotation.NonNull; -import android.support.design.widget.BottomSheetDialog; -import android.support.design.widget.BottomSheetDialogFragment; -import android.support.v4.widget.NestedScrollView; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.NonNull; +import com.google.android.material.bottomsheet.BottomSheetDialog; +import com.google.android.material.bottomsheet.BottomSheetDialogFragment; +import androidx.core.widget.NestedScrollView; +import androidx.appcompat.app.AppCompatActivity; import android.text.InputType; import android.util.JsonWriter; import android.view.View; diff --git a/app/src/main/java/com/termux/api/FingerprintAPI.java b/app/src/main/java/com/termux/api/FingerprintAPI.java index 878966312..f0bc6aef4 100644 --- a/app/src/main/java/com/termux/api/FingerprintAPI.java +++ b/app/src/main/java/com/termux/api/FingerprintAPI.java @@ -1,30 +1,30 @@ package com.termux.api; import android.annotation.TargetApi; -import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.hardware.fingerprint.FingerprintManager; import android.os.Build; import android.os.Bundle; -import android.os.CancellationSignal; import android.os.Handler; import android.os.Looper; -import android.security.keystore.KeyGenParameterSpec; -import android.security.keystore.KeyProperties; import android.util.JsonWriter; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.biometric.BiometricConstants; +import androidx.biometric.BiometricPrompt; +import androidx.core.hardware.fingerprint.FingerprintManagerCompat; +import androidx.fragment.app.FragmentActivity; + import com.termux.api.util.ResultReturner; import com.termux.api.util.TermuxApiLogger; -import java.security.KeyStore; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; -import javax.crypto.Cipher; -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; /** * This API allows users to use device fingerprint sensor as an authentication mechanism @@ -71,13 +71,12 @@ static void onReceive(final Context context, final Intent intent) { resetFingerprintResult(); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { - FingerprintManager fingerprintManager = (FingerprintManager)context.getSystemService(Context.FINGERPRINT_SERVICE); - + FingerprintManagerCompat fingerprintManagerCompat = FingerprintManagerCompat.from(context); // make sure we have a valid fingerprint sensor before attempting to launch Fingerprint activity - if (validateFingerprintSensor(context, fingerprintManager)) { + if (validateFingerprintSensor(context, fingerprintManagerCompat)) { Intent fingerprintIntent = new Intent(context, FingerprintActivity.class); fingerprintIntent.putExtras(intent.getExtras()); - fingerprintIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + fingerprintIntent.setFlags(FLAG_ACTIVITY_NEW_TASK); context.startActivity(fingerprintIntent); } else { postFingerprintResult(context, intent, fingerprintResult); @@ -121,16 +120,16 @@ public void writeJson(JsonWriter out) throws Exception { * Ensure that we have a fingerprint sensor and that the user has already enrolled fingerprints */ @TargetApi(Build.VERSION_CODES.M) - protected static boolean validateFingerprintSensor(Context context, FingerprintManager fingerprintManager) { + protected static boolean validateFingerprintSensor(Context context, FingerprintManagerCompat fingerprintManagerCompat) { boolean result = true; - if (!fingerprintManager.isHardwareDetected()) { + if (!fingerprintManagerCompat.isHardwareDetected()) { Toast.makeText(context, "No fingerprint scanner found!", Toast.LENGTH_SHORT).show(); appendFingerprintError(ERROR_NO_HARDWARE); result = false; } - if (!fingerprintManager.hasEnrolledFingerprints()) { + if (!fingerprintManagerCompat.hasEnrolledFingerprints()) { Toast.makeText(context, "No fingerprints enrolled", Toast.LENGTH_SHORT).show(); appendFingerprintError(ERROR_NO_ENROLLED_FINGERPRINTS); result = false; @@ -144,48 +143,30 @@ protected static boolean validateFingerprintSensor(Context context, FingerprintM * Activity that is necessary for authenticating w/ fingerprint sensor */ @TargetApi(Build.VERSION_CODES.M) - public static class FingerprintActivity extends Activity { + public static class FingerprintActivity extends FragmentActivity{ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); handleFingerprint(); - finish(); } /** * Handle setup and listening of fingerprint sensor */ protected void handleFingerprint() { - FingerprintManager fingerprintManager = (FingerprintManager)getSystemService(Context.FINGERPRINT_SERVICE); - Cipher cipher = null; - boolean hasError = false; - - try { - KeyStore keyStore = KeyStore.getInstance(KEYSTORE_NAME); - generateKey(keyStore); - cipher = getCipher(); - keyStore.load(null); - SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME, null); - cipher.init(Cipher.ENCRYPT_MODE, key); - } catch (Exception e) { - TermuxApiLogger.error(TAG, e); - hasError = true; - } - - if (cipher != null && !hasError) { - authenticateWithFingerprint(this, getIntent(), fingerprintManager, cipher); - } + Executor executor = Executors.newSingleThreadExecutor(); + authenticateWithFingerprint(this, getIntent(), executor); } /** * Handles authentication callback from our fingerprint sensor */ - protected static void authenticateWithFingerprint(final Context context, final Intent intent, final FingerprintManager fingerprintManager, Cipher cipher) { - FingerprintManager.AuthenticationCallback authenticationCallback = new FingerprintManager.AuthenticationCallback() { + protected static void authenticateWithFingerprint(final FragmentActivity context, final Intent intent, final Executor executor) { + BiometricPrompt biometricPrompt = new BiometricPrompt(context, executor, new BiometricPrompt.AuthenticationCallback() { @Override - public void onAuthenticationError(int errorCode, CharSequence errString) { - if (errorCode == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT) { + public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { + if (errorCode == BiometricConstants.ERROR_LOCKOUT) { appendFingerprintError(ERROR_LOCKOUT); // first time locked out, subsequent auth attempts will fail immediately for a bit @@ -199,7 +180,7 @@ public void onAuthenticationError(int errorCode, CharSequence errString) { } @Override - public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { + public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) { setAuthResult(AUTH_RESULT_SUCCESS); postFingerprintResult(context, intent, fingerprintResult); } @@ -208,71 +189,38 @@ public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult re public void onAuthenticationFailed() { addFailedAttempt(); } + }); - // unused - @Override - public void onAuthenticationHelp(int helpCode, CharSequence helpString) { } - }; - - Toast.makeText(context, "Scan fingerprint", Toast.LENGTH_LONG).show(); + BiometricPrompt.PromptInfo.Builder builder = new BiometricPrompt.PromptInfo.Builder(); + builder.setTitle(intent.hasExtra("title") ? intent.getStringExtra("title") : "Authenticate"); + builder.setNegativeButtonText(intent.hasExtra("cancel") ? intent.getStringExtra("cancel") : "Cancel"); + if (intent.hasExtra("description")) { + builder.setDescription(intent.getStringExtra("description")); + } + if (intent.hasExtra("subtitle")) { + builder.setSubtitle(intent.getStringExtra("subtitle")); + } // listen to fingerprint sensor - FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher); - final CancellationSignal cancellationSignal = new CancellationSignal(); - fingerprintManager.authenticate(cryptoObject, cancellationSignal, 0, authenticationCallback, null); + biometricPrompt.authenticate(builder.build()); - addSensorTimeout(context, intent, cancellationSignal); + addSensorTimeout(context, intent, biometricPrompt); } /** * Adds a timeout for our fingerprint sensor which will force a result return if we * haven't already received one */ - protected static void addSensorTimeout(final Context context, final Intent intent, final CancellationSignal cancellationSignal) { + protected static void addSensorTimeout(final Context context, final Intent intent, final BiometricPrompt biometricPrompt) { final Handler timeoutHandler = new Handler(Looper.getMainLooper()); timeoutHandler.postDelayed(() -> { if (!postedResult) { appendFingerprintError(ERROR_TIMEOUT); - cancellationSignal.cancel(); + biometricPrompt.cancelAuthentication(); postFingerprintResult(context, intent, fingerprintResult); } }, SENSOR_TIMEOUT); } - - protected static void generateKey(KeyStore keyStore) { - try { - KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_NAME); - keyStore.load(null); - keyGenerator.init( - new KeyGenParameterSpec.Builder(KEY_NAME, - KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) - .setBlockModes(KeyProperties.BLOCK_MODE_CBC) - .setUserAuthenticationRequired(true) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) - .build()); - - keyGenerator.generateKey(); - } catch (Exception e) { - TermuxApiLogger.error(TAG, e); - appendFingerprintError(ERROR_KEY_GENERATOR); - } - } - - /** - * Create the cipher needed for use with our SecretKey - */ - protected static Cipher getCipher() { - Cipher cipher = null; - try { - cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES - + "/" + KeyProperties.BLOCK_MODE_CBC - + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7); - } catch (Exception e) { - TermuxApiLogger.error(TAG, e); - appendFingerprintError(ERROR_CIPHER); - } - return cipher; - } } /** diff --git a/app/src/main/java/com/termux/api/JobSchedulerAPI.java b/app/src/main/java/com/termux/api/JobSchedulerAPI.java index 000b38958..1feca8e79 100644 --- a/app/src/main/java/com/termux/api/JobSchedulerAPI.java +++ b/app/src/main/java/com/termux/api/JobSchedulerAPI.java @@ -7,7 +7,7 @@ import android.content.Intent; import android.os.Build; import android.os.PersistableBundle; -import android.support.annotation.RequiresApi; +import androidx.annotation.RequiresApi; import android.text.TextUtils; import android.util.Log; diff --git a/app/src/main/java/com/termux/api/KeystoreAPI.java b/app/src/main/java/com/termux/api/KeystoreAPI.java index 737fa4871..22bea18c3 100644 --- a/app/src/main/java/com/termux/api/KeystoreAPI.java +++ b/app/src/main/java/com/termux/api/KeystoreAPI.java @@ -6,7 +6,7 @@ import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyInfo; import android.security.keystore.KeyProperties; -import android.support.annotation.RequiresApi; +import androidx.annotation.RequiresApi; import android.util.Base64; import android.util.JsonWriter; diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 0db188057..62c845834 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -17,6 +17,19 @@ true + + +