From 6661d457ea9d38bffb31b85f1ec891e978494b75 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 23 Dec 2023 20:41:36 +0100 Subject: [PATCH] feat: Add AsLockedApp wrapper to require id verification if enabled --- .../java/app/myzel394/alibi/MainActivity.kt | 6 +- .../myzel394/alibi/helpers/AppLockHelper.kt | 12 +++ .../alibi/ui/{LockedApp.kt => AsLockedApp.kt} | 77 ++++++++++++++++++- .../java/app/myzel394/alibi/ui/Navigation.kt | 6 -- 4 files changed, 90 insertions(+), 11 deletions(-) rename app/src/main/java/app/myzel394/alibi/ui/{LockedApp.kt => AsLockedApp.kt} (58%) diff --git a/app/src/main/java/app/myzel394/alibi/MainActivity.kt b/app/src/main/java/app/myzel394/alibi/MainActivity.kt index 7222997..45e5c82 100644 --- a/app/src/main/java/app/myzel394/alibi/MainActivity.kt +++ b/app/src/main/java/app/myzel394/alibi/MainActivity.kt @@ -14,7 +14,7 @@ import androidx.core.view.WindowCompat import androidx.datastore.dataStore import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.AppSettingsSerializer -import app.myzel394.alibi.ui.LockedApp +import app.myzel394.alibi.ui.AsLockedApp import app.myzel394.alibi.ui.Navigation import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY import app.myzel394.alibi.ui.theme.AlibiTheme @@ -33,7 +33,9 @@ class MainActivity : AppCompatActivity() { setContent { AlibiTheme { - Navigation() + AsLockedApp { + Navigation() + } } } } diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt b/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt index 182172e..5f10137 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt @@ -1,11 +1,13 @@ package app.myzel394.alibi.helpers +import android.app.Activity import android.content.Context import androidx.biometric.BiometricManager import androidx.core.content.ContextCompat import androidx.biometric.BiometricPrompt import androidx.fragment.app.FragmentActivity import kotlinx.coroutines.CompletableDeferred +import kotlin.system.exitProcess class AppLockHelper { enum class SupportType { @@ -60,5 +62,15 @@ class AppLockHelper { return deferred } + + fun closeApp(context: Context) { + (context as? Activity)?.let { + it.finishAndRemoveTask() + it.finishAffinity() + it.finish() + } + + exitProcess(0) + } } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/LockedApp.kt b/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt similarity index 58% rename from app/src/main/java/app/myzel394/alibi/ui/LockedApp.kt rename to app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt index b152ab8..48ec3fc 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/LockedApp.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt @@ -20,17 +20,88 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import app.myzel394.alibi.R +import app.myzel394.alibi.dataStore +import app.myzel394.alibi.helpers.AppLockHelper +import kotlinx.coroutines.launch // After this amount, close the app -const val MAX_TRIES = 5 +const val MAX_TRIES = 10 @Composable -fun LockedApp() { +fun AsLockedApp( + content: (@Composable () -> Unit), +) { + val scope = rememberCoroutineScope() + val context = LocalContext.current + + val settings = context + .dataStore + .data + .collectAsState(initial = null) + .value ?: return + + // -1 = Unlocked, any other value = locked + var tries by remember { mutableIntStateOf(0) } + + LaunchedEffect(settings.isAppLockEnabled()) { + if (!settings.isAppLockEnabled()) { + tries = -1 + } + } + + if (tries == -1) { + return content() + } + + val title = stringResource(R.string.identityVerificationRequired_title) + val subtitle = stringResource(R.string.identityVerificationRequired_subtitle) + + fun openAuthentication() { + if (tries >= MAX_TRIES) { + AppLockHelper.closeApp(context) + return + } + + scope.launch { + val successful = AppLockHelper.authenticate( + context, + title, + subtitle, + ).await() + + if (successful) { + tries = -1 + return@launch + } + + tries++ + + if (tries >= MAX_TRIES) { + AppLockHelper.closeApp(context) + } + } + } + + LaunchedEffect(settings.isAppLockEnabled()) { + if (settings.isAppLockEnabled()) { + openAuthentication() + } + } + Scaffold { paddingValues -> Column( modifier = Modifier @@ -60,7 +131,7 @@ fun LockedApp() { modifier = Modifier .fillMaxWidth() .height(BIG_PRIMARY_BUTTON_SIZE), - onClick = {}, + onClick = ::openAuthentication, colors = ButtonDefaults.filledTonalButtonColors(), ) { Icon( diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt index bc1aa2a..a352502 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt @@ -72,12 +72,6 @@ fun Navigation( } } - LaunchedEffect(Unit) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - AppLockHelper.authenticate(context, "Title", "Subtitle") - } - } - LaunchedEffect(settings.theme) { if (!SUPPORTS_DARK_MODE_NATIVELY) { val currentValue = AppCompatDelegate.getDefaultNightMode()