diff --git a/briar-android/build.gradle b/briar-android/build.gradle index bb4988e825110c3dd3de940a1a6a73311c485924..ef9427921fe63c69a84009ffda49f91078a66808 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -75,8 +75,8 @@ def getStdout = { command, defaultValue -> } android { - compileSdkVersion 27 - buildToolsVersion '27.0.3' + compileSdkVersion 28 + buildToolsVersion '28.0.2' defaultConfig { minSdkVersion 15 diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml index 82d4b83cfca31940105b2a64a3b4b57544bba5b8..79eb8b41a5dd723babfa06d22499a193d1e3865f 100644 --- a/briar-android/src/main/AndroidManifest.xml +++ b/briar-android/src/main/AndroidManifest.xml @@ -17,6 +17,7 @@ + requestKeyguardUnlock()); - + if (!hasUsableFingerprint(this)) { + getWindow().setBackgroundDrawable(null); + findViewById(R.id.image).setVisibility(INVISIBLE); + } keyguardShown = state != null && state.getBoolean(KEYGUARD_SHOWN); } @@ -83,18 +94,79 @@ public class UnlockActivity extends BaseActivity { // Check if app is still locked, lockable // and not finishing (which is possible if recreated) if (!keyguardShown && lockManager.isLocked() && !isFinishing()) { - requestKeyguardUnlock(); + requestUnlock(); } else if (!lockManager.isLocked()) { setResult(RESULT_OK); finish(); } } + private void requestUnlock() { + if (SDK_INT >= 28 && hasUsableFingerprint(this)) { + requestFingerprintUnlock(); + } else { + requestKeyguardUnlock(); + } + } + @Override public void onBackPressed() { 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() { KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index 7fb316a622e387df0c0b837f2a2ee857f96196ce..4cb49aac5d7a00752ef32e646fd0c32d8f10504f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -14,6 +14,7 @@ import android.support.annotation.ColorRes; import android.support.design.widget.TextInputLayout; import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; +import android.support.v4.hardware.fingerprint.FingerprintManagerCompat; import android.support.v7.app.AlertDialog; import android.text.Html; import android.text.Spannable; @@ -261,6 +262,10 @@ public class UiUtils { } public static boolean hasScreenLock(Context ctx) { + return hasKeyguardLock(ctx) || hasUsableFingerprint(ctx); + } + + public static boolean hasKeyguardLock(Context ctx) { if (SDK_INT < 21) return false; KeyguardManager keyguardManager = (KeyguardManager) ctx.getSystemService(KEYGUARD_SERVICE); @@ -271,6 +276,12 @@ public class UiUtils { (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) { androidExecutor.runOnBackgroundThread( () -> ACRA.getErrorReporter() diff --git a/briar-android/src/main/res/layout/activity_unlock.xml b/briar-android/src/main/res/layout/activity_unlock.xml index 48413a19820d5251e01c1418ffd54fb48aedcb09..83c8d594b103d7bfd9d57dc849240888a07e9f19 100644 --- a/briar-android/src/main/res/layout/activity_unlock.xml +++ b/briar-android/src/main/res/layout/activity_unlock.xml @@ -14,36 +14,11 @@ android:layout_height="150dp" android:layout_margin="@dimen/margin_large" android:src="@drawable/splash_screen" - app:layout_constraintBottom_toTopOf="@+id/is_locked" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintVertical_chainStyle="spread" + app:layout_constraintVertical_bias="0.1" app:tint="?attr/colorControlNormal"/> - - -