mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 06:39:54 +01:00
Merge branch '1247-pin-lock-fingerprint' into 'master'
Implement fingerprint unlocking with BiometricPromptCompat See merge request briar/briar!882
This commit is contained in:
@@ -75,8 +75,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
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<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_BIOMETRIC" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="org.briarproject.briar.android.BriarApplicationImpl"
|
android:name="org.briarproject.briar.android.BriarApplicationImpl"
|
||||||
|
|||||||
@@ -2,10 +2,15 @@ package org.briarproject.briar.android.login;
|
|||||||
|
|
||||||
import android.app.KeyguardManager;
|
import android.app.KeyguardManager;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.hardware.biometrics.BiometricPrompt;
|
||||||
|
import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback;
|
||||||
|
import android.hardware.biometrics.BiometricPrompt.AuthenticationResult;
|
||||||
|
import android.hardware.biometrics.BiometricPrompt.Builder;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
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.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 +23,13 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.hardware.biometrics.BiometricPrompt.BIOMETRIC_ERROR_CANCELED;
|
||||||
|
import static android.hardware.biometrics.BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED;
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static android.view.View.INVISIBLE;
|
||||||
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
|
||||||
@@ -45,9 +55,10 @@ public class UnlockActivity extends BaseActivity {
|
|||||||
overridePendingTransition(0, 0);
|
overridePendingTransition(0, 0);
|
||||||
setContentView(R.layout.activity_unlock);
|
setContentView(R.layout.activity_unlock);
|
||||||
|
|
||||||
Button button = findViewById(R.id.unlock);
|
if (!hasUsableFingerprint(this)) {
|
||||||
button.setOnClickListener(view -> requestKeyguardUnlock());
|
getWindow().setBackgroundDrawable(null);
|
||||||
|
findViewById(R.id.image).setVisibility(INVISIBLE);
|
||||||
|
}
|
||||||
keyguardShown = state != null && state.getBoolean(KEYGUARD_SHOWN);
|
keyguardShown = state != null && state.getBoolean(KEYGUARD_SHOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,18 +94,79 @@ 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 (SDK_INT >= 28 && hasUsableFingerprint(this)) {
|
||||||
|
requestFingerprintUnlock();
|
||||||
|
} else {
|
||||||
|
requestKeyguardUnlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
moveTaskToBack(true);
|
moveTaskToBack(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = 28)
|
||||||
|
private void requestFingerprintUnlock() {
|
||||||
|
BiometricPrompt biometricPrompt = new Builder(this)
|
||||||
|
.setTitle(getString(R.string.lock_unlock))
|
||||||
|
.setDescription(
|
||||||
|
getString(R.string.lock_unlock_fingerprint_description))
|
||||||
|
.setNegativeButton(getString(R.string.lock_unlock_password),
|
||||||
|
getMainExecutor(),
|
||||||
|
(dialog, which) -> requestKeyguardUnlock())
|
||||||
|
.build();
|
||||||
|
CancellationSignal signal = new CancellationSignal();
|
||||||
|
AuthenticationCallback callback = new AuthenticationCallback() {
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
// e.g. 5 failed attempts
|
||||||
|
else {
|
||||||
|
if (hasKeyguardLock(UnlockActivity.this)) {
|
||||||
|
requestKeyguardUnlock();
|
||||||
|
} else {
|
||||||
|
// normally fingerprints require a screen lock, but
|
||||||
|
// who knows if that's true for all devices out there
|
||||||
|
if (errString != null) {
|
||||||
|
Toast.makeText(UnlockActivity.this, errString,
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationHelp(int helpCode,
|
||||||
|
@Nullable CharSequence helpString) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationSucceeded(AuthenticationResult result) {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationFailed() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
biometricPrompt.authenticate(signal, getMainExecutor(), callback);
|
||||||
|
}
|
||||||
|
|
||||||
private void requestKeyguardUnlock() {
|
private void requestKeyguardUnlock() {
|
||||||
KeyguardManager keyguardManager =
|
KeyguardManager keyguardManager =
|
||||||
(KeyguardManager) getSystemService(KEYGUARD_SERVICE);
|
(KeyguardManager) getSystemService(KEYGUARD_SERVICE);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import android.support.annotation.ColorRes;
|
|||||||
import android.support.design.widget.TextInputLayout;
|
import android.support.design.widget.TextInputLayout;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.support.v4.hardware.fingerprint.FingerprintManagerCompat;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
@@ -261,6 +262,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 +276,12 @@ public class UiUtils {
|
|||||||
(SDK_INT >= 23 && keyguardManager.isDeviceSecure());
|
(SDK_INT >= 23 && keyguardManager.isDeviceSecure());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean hasUsableFingerprint(Context ctx) {
|
||||||
|
if (SDK_INT < 28) return false;
|
||||||
|
FingerprintManagerCompat fm = FingerprintManagerCompat.from(ctx);
|
||||||
|
return fm.hasEnrolledFingerprints() && fm.isHardwareDetected();
|
||||||
|
}
|
||||||
|
|
||||||
public static void triggerFeedback(AndroidExecutor androidExecutor) {
|
public static void triggerFeedback(AndroidExecutor androidExecutor) {
|
||||||
androidExecutor.runOnBackgroundThread(
|
androidExecutor.runOnBackgroundThread(
|
||||||
() -> ACRA.getErrorReporter()
|
() -> ACRA.getErrorReporter()
|
||||||
|
|||||||
@@ -14,36 +14,11 @@
|
|||||||
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_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintVertical_chainStyle="spread"
|
app:layout_constraintVertical_bias="0.1"
|
||||||
app:tint="?attr/colorControlNormal"/>
|
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_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/image"
|
|
||||||
app:layout_constraintVertical_chainStyle="spread"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/unlock"
|
|
||||||
style="@style/BriarButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="@dimen/margin_large"
|
|
||||||
android:text="@string/lock_unlock"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"/>
|
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user