mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-22 07:39:53 +01:00
ScreenLock: Implement fingerprint unlocking with BiometricPromptCompat
This commit is contained in:
@@ -33,6 +33,7 @@ dependencies {
|
|||||||
implementation 'com.google.zxing:core:3.3.0'
|
implementation 'com.google.zxing:core:3.3.0'
|
||||||
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.8.0'
|
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.8.0'
|
||||||
implementation 'com.vanniktech:emoji-google:0.5.1'
|
implementation 'com.vanniktech:emoji-google:0.5.1'
|
||||||
|
implementation 'moe.feng.support.biometricprompt:library:1.0.1'
|
||||||
|
|
||||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
|
|
||||||
@@ -75,8 +76,8 @@ def getStdout = { command, defaultValue ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdkVersion 28
|
||||||
buildToolsVersion '27.0.3'
|
buildToolsVersion '28.0.2'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest
|
<manifest
|
||||||
package="org.briarproject.briar"
|
package="org.briarproject.briar"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-sdk tools:overrideLibrary="moe.feng.support.biometricprompt"/>
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
|
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||||
@@ -17,6 +20,8 @@
|
|||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||||
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
||||||
|
<uses-permission-sdk-23 android:name="android.permission.USE_FINGERPRINT" />
|
||||||
|
<uses-permission-sdk-23 android:name="android.permission.USE_BIOMETRIC" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="org.briarproject.briar.android.BriarApplicationImpl"
|
android:name="org.briarproject.briar.android.BriarApplicationImpl"
|
||||||
|
|||||||
@@ -3,9 +3,12 @@ package org.briarproject.briar.android.login;
|
|||||||
import android.app.KeyguardManager;
|
import android.app.KeyguardManager;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.RequiresApi;
|
import android.support.annotation.RequiresApi;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
@@ -18,8 +21,19 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import moe.feng.support.biometricprompt.BiometricPromptCompat;
|
||||||
|
import moe.feng.support.biometricprompt.BiometricPromptCompat.Builder;
|
||||||
|
import moe.feng.support.biometricprompt.BiometricPromptCompat.IAuthenticationCallback;
|
||||||
|
import moe.feng.support.biometricprompt.BiometricPromptCompat.IAuthenticationResult;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static moe.feng.support.biometricprompt.BiometricPromptCompat.BIOMETRIC_ERROR_CANCELED;
|
||||||
|
import static moe.feng.support.biometricprompt.BiometricPromptCompat.BIOMETRIC_ERROR_LOCKOUT;
|
||||||
|
import static moe.feng.support.biometricprompt.BiometricPromptCompat.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
|
||||||
|
import static moe.feng.support.biometricprompt.BiometricPromptCompat.BIOMETRIC_ERROR_USER_CANCELED;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_KEYGUARD_UNLOCK;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_KEYGUARD_UNLOCK;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.hasKeyguardLock;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.hasUsableFingerprint;
|
||||||
|
|
||||||
@RequiresApi(21)
|
@RequiresApi(21)
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@@ -46,7 +60,7 @@ public class UnlockActivity extends BaseActivity {
|
|||||||
setContentView(R.layout.activity_unlock);
|
setContentView(R.layout.activity_unlock);
|
||||||
|
|
||||||
Button button = findViewById(R.id.unlock);
|
Button button = findViewById(R.id.unlock);
|
||||||
button.setOnClickListener(view -> requestKeyguardUnlock());
|
button.setOnClickListener(view -> requestUnlock());
|
||||||
|
|
||||||
keyguardShown = state != null && state.getBoolean(KEYGUARD_SHOWN);
|
keyguardShown = state != null && state.getBoolean(KEYGUARD_SHOWN);
|
||||||
}
|
}
|
||||||
@@ -83,18 +97,80 @@ public class UnlockActivity extends BaseActivity {
|
|||||||
// Check if app is still locked, lockable
|
// Check if app is still locked, lockable
|
||||||
// and not finishing (which is possible if recreated)
|
// and not finishing (which is possible if recreated)
|
||||||
if (!keyguardShown && lockManager.isLocked() && !isFinishing()) {
|
if (!keyguardShown && lockManager.isLocked() && !isFinishing()) {
|
||||||
requestKeyguardUnlock();
|
requestUnlock();
|
||||||
} else if (!lockManager.isLocked()) {
|
} else if (!lockManager.isLocked()) {
|
||||||
setResult(RESULT_OK);
|
setResult(RESULT_OK);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void requestUnlock() {
|
||||||
|
if (hasUsableFingerprint(this)) {
|
||||||
|
requestFingerprintUnlock();
|
||||||
|
} else {
|
||||||
|
requestKeyguardUnlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
moveTaskToBack(true);
|
moveTaskToBack(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void requestFingerprintUnlock() {
|
||||||
|
BiometricPromptCompat biometricPrompt = new Builder(this)
|
||||||
|
.setTitle(R.string.lock_unlock)
|
||||||
|
.setDescription(R.string.lock_unlock_fingerprint_description)
|
||||||
|
.setNegativeButton(R.string.lock_unlock_password,
|
||||||
|
(dialog, which) -> {
|
||||||
|
requestKeyguardUnlock();
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
CancellationSignal signal = new CancellationSignal();
|
||||||
|
biometricPrompt.authenticate(signal, new IAuthenticationCallback() {
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationError(int errorCode,
|
||||||
|
@Nullable CharSequence errString) {
|
||||||
|
// when back button is pressed while fingerprint dialog shows
|
||||||
|
if (errorCode == BIOMETRIC_ERROR_CANCELED ||
|
||||||
|
errorCode == BIOMETRIC_ERROR_USER_CANCELED) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
// locked out due to 5 failed attempts, lasts for 30 seconds
|
||||||
|
else if (errorCode == BIOMETRIC_ERROR_LOCKOUT ||
|
||||||
|
errorCode == BIOMETRIC_ERROR_LOCKOUT_PERMANENT) {
|
||||||
|
if (hasKeyguardLock(UnlockActivity.this)) {
|
||||||
|
requestKeyguardUnlock();
|
||||||
|
} else {
|
||||||
|
// normally fingerprints require a screen lock, but
|
||||||
|
// who knows if that's true for all devices out there
|
||||||
|
Toast.makeText(UnlockActivity.this, errString,
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Toast.makeText(UnlockActivity.this, errString,
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationHelp(int helpCode,
|
||||||
|
@Nullable CharSequence helpString) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationSucceeded(
|
||||||
|
@NonNull IAuthenticationResult result) {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationFailed() {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void requestKeyguardUnlock() {
|
private void requestKeyguardUnlock() {
|
||||||
KeyguardManager keyguardManager =
|
KeyguardManager keyguardManager =
|
||||||
(KeyguardManager) getSystemService(KEYGUARD_SERVICE);
|
(KeyguardManager) getSystemService(KEYGUARD_SERVICE);
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ import static android.text.format.DateUtils.FORMAT_ABBREV_TIME;
|
|||||||
import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
|
import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
|
||||||
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
|
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
|
||||||
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
|
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
|
||||||
|
import static moe.feng.support.biometricprompt.BiometricPromptCompat.hasEnrolledFingerprints;
|
||||||
|
import static moe.feng.support.biometricprompt.BiometricPromptCompat.isHardwareDetected;
|
||||||
import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
|
import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
|
||||||
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
|
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
|
||||||
|
|
||||||
@@ -261,6 +263,10 @@ public class UiUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasScreenLock(Context ctx) {
|
public static boolean hasScreenLock(Context ctx) {
|
||||||
|
return hasKeyguardLock(ctx) || hasUsableFingerprint(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasKeyguardLock(Context ctx) {
|
||||||
if (SDK_INT < 21) return false;
|
if (SDK_INT < 21) return false;
|
||||||
KeyguardManager keyguardManager =
|
KeyguardManager keyguardManager =
|
||||||
(KeyguardManager) ctx.getSystemService(KEYGUARD_SERVICE);
|
(KeyguardManager) ctx.getSystemService(KEYGUARD_SERVICE);
|
||||||
@@ -271,6 +277,11 @@ public class UiUtils {
|
|||||||
(SDK_INT >= 23 && keyguardManager.isDeviceSecure());
|
(SDK_INT >= 23 && keyguardManager.isDeviceSecure());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean hasUsableFingerprint(Context ctx) {
|
||||||
|
return SDK_INT >= 23 && isHardwareDetected(ctx) &&
|
||||||
|
hasEnrolledFingerprints(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
public static void triggerFeedback(AndroidExecutor androidExecutor) {
|
public static void triggerFeedback(AndroidExecutor androidExecutor) {
|
||||||
androidExecutor.runOnBackgroundThread(
|
androidExecutor.runOnBackgroundThread(
|
||||||
() -> ACRA.getErrorReporter()
|
() -> ACRA.getErrorReporter()
|
||||||
|
|||||||
@@ -14,26 +14,12 @@
|
|||||||
android:layout_height="150dp"
|
android:layout_height="150dp"
|
||||||
android:layout_margin="@dimen/margin_large"
|
android:layout_margin="@dimen/margin_large"
|
||||||
android:src="@drawable/splash_screen"
|
android:src="@drawable/splash_screen"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/is_locked"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_chainStyle="spread"
|
|
||||||
app:tint="?attr/colorControlNormal"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/is_locked"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="@dimen/margin_large"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/lock_is_locked"
|
|
||||||
android:textSize="@dimen/text_size_xlarge"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/unlock"
|
app:layout_constraintBottom_toTopOf="@+id/unlock"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/image"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintVertical_chainStyle="spread"/>
|
app:layout_constraintVertical_bias="0.1"
|
||||||
|
app:tint="?attr/colorControlNormal"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/unlock"
|
android:id="@+id/unlock"
|
||||||
|
|||||||
@@ -475,6 +475,8 @@
|
|||||||
<!-- App Locking -->
|
<!-- App Locking -->
|
||||||
<string name="lock_unlock">Unlock Briar</string>
|
<string name="lock_unlock">Unlock Briar</string>
|
||||||
<string name="lock_unlock_verbose">Enter your device PIN, pattern or password to unlock Briar</string>
|
<string name="lock_unlock_verbose">Enter your device PIN, pattern or password to unlock Briar</string>
|
||||||
|
<string name="lock_unlock_fingerprint_description">Touch your fingerprint sensor with the registered finger to continue</string>
|
||||||
|
<string name="lock_unlock_password">Use Password</string>
|
||||||
<string name="lock_is_locked">Briar is locked</string>
|
<string name="lock_is_locked">Briar is locked</string>
|
||||||
<string name="lock_tap_to_unlock">Tap to unlock</string>
|
<string name="lock_tap_to_unlock">Tap to unlock</string>
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ dependencyVerification {
|
|||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2',
|
'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2',
|
||||||
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||||
|
'moe.feng.support.biometricprompt:library:1.0.1:library-1.0.1.aar:2cf011abdc9c51a505b35f421197b4fbeba79af792e9ea1ca973f1aef087dacd',
|
||||||
'nekohtml:nekohtml:1.9.6.2:nekohtml-1.9.6.2.jar:fdff6cfa9ed9cc911c842a5d2395f209ec621ef1239d46810e9e495809d3ae09',
|
'nekohtml:nekohtml:1.9.6.2:nekohtml-1.9.6.2.jar:fdff6cfa9ed9cc911c842a5d2395f209ec621ef1239d46810e9e495809d3ae09',
|
||||||
'nekohtml:xercesMinimal:1.9.6.2:xercesMinimal-1.9.6.2.jar:95b8b357d19f63797dd7d67622fd3f18374d64acbc6584faba1c7759a31e8438',
|
'nekohtml:xercesMinimal:1.9.6.2:xercesMinimal-1.9.6.2.jar:95b8b357d19f63797dd7d67622fd3f18374d64acbc6584faba1c7759a31e8438',
|
||||||
'net.bytebuddy:byte-buddy-agent:1.7.9:byte-buddy-agent-1.7.9.jar:ac1a993befb528c3271a83a9ad9c42d363d399e9deb26e0470e3c4962066c550',
|
'net.bytebuddy:byte-buddy-agent:1.7.9:byte-buddy-agent-1.7.9.jar:ac1a993befb528c3271a83a9ad9c42d363d399e9deb26e0470e3c4962066c550',
|
||||||
|
|||||||
Reference in New Issue
Block a user