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
+
+
+
-
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index d7e81f80f..1a04b66fb 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -17,3 +17,5 @@ org.gradle.jvmargs=-Xmx2048M
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
+android.enableJetifier=true
+android.useAndroidX=true
\ No newline at end of file