diff --git a/client/android/app/build.gradle b/client/android/app/build.gradle index 38c23a189..d936d41a0 100644 --- a/client/android/app/build.gradle +++ b/client/android/app/build.gradle @@ -56,6 +56,7 @@ dependencies { implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion" implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion" implementation "androidx.browser:browser:$androidxBrowserVersion" + implementation "androidx.biometric:biometric:1.1.0" implementation project(':capacitor-android') testImplementation "junit:junit:$junitVersion" androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" diff --git a/client/android/app/src/main/java/com/liketrek/trek/TrekShellPlugin.java b/client/android/app/src/main/java/com/liketrek/trek/TrekShellPlugin.java index a12975fcd..b5ee2ec61 100644 --- a/client/android/app/src/main/java/com/liketrek/trek/TrekShellPlugin.java +++ b/client/android/app/src/main/java/com/liketrek/trek/TrekShellPlugin.java @@ -31,7 +31,7 @@ public class TrekShellPlugin extends Plugin { */ static final int SHELL_API = 2; /** The native features by name, so the UI can turn each on by itself. */ - static final String[] FEATURES = { "capabilities" }; + static final String[] FEATURES = { "capabilities", "biometricLock" }; @PluginMethod public void getCapabilities(PluginCall call) { @@ -55,6 +55,72 @@ public class TrekShellPlugin extends Plugin { call.resolve(result); } + // ---- App lock ---- + + private static final int AUTHENTICATORS = + androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK + | androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL; + + /** Which biometry the device offers for the app lock, if any. */ + @PluginMethod + public void biometryInfo(PluginCall call) { + int status = androidx.biometric.BiometricManager.from(getContext()).canAuthenticate(AUTHENTICATORS); + JSObject result = new JSObject(); + result.put("available", status == androidx.biometric.BiometricManager.BIOMETRIC_SUCCESS); + result.put("type", "biometric"); + call.resolve(result); + } + + /** The system biometric prompt with the screen lock as the way out. */ + @PluginMethod + public void authenticateBiometric(PluginCall call) { + String reason = call.getString("reason", "Unlock TREK"); + getActivity().runOnUiThread(() -> { + androidx.fragment.app.FragmentActivity activity = (androidx.fragment.app.FragmentActivity) getActivity(); + androidx.biometric.BiometricPrompt prompt = new androidx.biometric.BiometricPrompt( + activity, + androidx.core.content.ContextCompat.getMainExecutor(getContext()), + new androidx.biometric.BiometricPrompt.AuthenticationCallback() { + @Override + public void onAuthenticationSucceeded(androidx.biometric.BiometricPrompt.AuthenticationResult result) { + JSObject ok = new JSObject(); + ok.put("ok", true); + call.resolve(ok); + } + + @Override + public void onAuthenticationError(int code, CharSequence message) { + boolean cancelled = code == androidx.biometric.BiometricPrompt.ERROR_USER_CANCELED + || code == androidx.biometric.BiometricPrompt.ERROR_NEGATIVE_BUTTON + || code == androidx.biometric.BiometricPrompt.ERROR_CANCELED; + call.reject(message.toString(), cancelled ? "CANCELLED" : "FAILED"); + } + } + ); + androidx.biometric.BiometricPrompt.PromptInfo info = new androidx.biometric.BiometricPrompt.PromptInfo.Builder() + .setTitle(reason) + .setAllowedAuthenticators(AUTHENTICATORS) + .build(); + prompt.authenticate(info); + }); + } + + /** While on, the recent-apps screen shows no snapshot of the app. */ + @PluginMethod + public void setPrivacyCover(PluginCall call) { + boolean enabled = Boolean.TRUE.equals(call.getBoolean("enabled", false)); + getActivity().runOnUiThread(() -> { + if (android.os.Build.VERSION.SDK_INT >= 33) { + getActivity().setRecentsScreenshotEnabled(!enabled); + } else if (enabled) { + getActivity().getWindow().addFlags(android.view.WindowManager.LayoutParams.FLAG_SECURE); + } else { + getActivity().getWindow().clearFlags(android.view.WindowManager.LayoutParams.FLAG_SECURE); + } + }); + call.resolve(); + } + @PluginMethod public void getServer(PluginCall call) { String url = TrekServerStore.getUrl(getContext()); diff --git a/client/ios/App/App/Info.plist b/client/ios/App/App/Info.plist index b4d095119..88d841452 100644 --- a/client/ios/App/App/Info.plist +++ b/client/ios/App/App/Info.plist @@ -71,6 +71,8 @@ NSPhotoLibraryAddUsageDescription TREK saves photos and exports you download from your trips back to your photo library. + NSFaceIDUsageDescription + TREK uses Face ID to unlock the app when you turn on the app lock. NSLocationWhenInUseUsageDescription TREK uses your location to centre the map on you and to find places near you.