Lock Android app on background for biometrics

This commit is contained in:
2026-04-23 15:07:16 -05:00
parent 5f27b1562e
commit d80d537d02
6 changed files with 33 additions and 6 deletions
+6 -1
View File
@@ -2,10 +2,15 @@
## Versión actual ## Versión actual
- Versión base: `1.2.4` - Versión base: `1.2.5`
## Historial ## Historial
### 1.2.5 - 2026-04-23
- Android vuelve a bloquearse al pasar a segundo plano cuando la biometría está activa, para que al regresar sí pida huella.
- Se publica APK nuevo con versión visible `1.2.5`.
### 1.2.4 - 2026-04-23 ### 1.2.4 - 2026-04-23
- Android corrige el cierre de sesión para volver a ofrecer la pantalla biométrica cuando existen credenciales biométricas guardadas. - Android corrige el cierre de sesión para volver a ofrecer la pantalla biométrica cuando existen credenciales biométricas guardadas.
+1 -1
View File
@@ -1 +1 @@
1.2.4 1.2.5
+2 -2
View File
@@ -18,8 +18,8 @@ android {
applicationId "net.linkedcorp.linkedsign" applicationId "net.linkedcorp.linkedsign"
minSdk 26 minSdk 26
targetSdk 35 targetSdk 35
versionCode 10204 versionCode 10205
versionName "1.2.4" versionName "1.2.5"
buildConfigField "String", "API_BASE_URL", "\"${apiBaseUrl}\"" buildConfigField "String", "API_BASE_URL", "\"${apiBaseUrl}\""
} }
@@ -95,6 +95,7 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@@ -113,6 +114,9 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.PasswordVisualTransformation
@@ -209,6 +213,7 @@ fun LinkedSignApp(viewModel: MainViewModel, appContext: Context, activity: Compo
var selectedTab by rememberSaveable { mutableStateOf(AppTab.Documents) } var selectedTab by rememberSaveable { mutableStateOf(AppTab.Documents) }
var pendingSignDocumentId by rememberSaveable { mutableStateOf<String?>(null) } var pendingSignDocumentId by rememberSaveable { mutableStateOf<String?>(null) }
val biometricAvailable = remember(activity) { canUseBiometricAccess(activity) } val biometricAvailable = remember(activity) { canUseBiometricAccess(activity) }
val lifecycleOwner = LocalLifecycleOwner.current
var appUnlocked by rememberSaveable { mutableStateOf(false) } var appUnlocked by rememberSaveable { mutableStateOf(false) }
var bootstrapGateHandled by rememberSaveable { mutableStateOf(false) } var bootstrapGateHandled by rememberSaveable { mutableStateOf(false) }
var showBiometricOptIn by rememberSaveable { mutableStateOf(false) } var showBiometricOptIn by rememberSaveable { mutableStateOf(false) }
@@ -235,6 +240,23 @@ fun LinkedSignApp(viewModel: MainViewModel, appContext: Context, activity: Compo
} }
} }
DisposableEffect(lifecycleOwner, biometricAvailable, state.biometricEnabled, state.biometricHasCredentials) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_STOP &&
biometricAvailable &&
state.biometricEnabled &&
state.biometricHasCredentials
) {
appUnlocked = false
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
Scaffold( Scaffold(
modifier = Modifier.pointerInput(state.authenticated) { modifier = Modifier.pointerInput(state.authenticated) {
if (!state.authenticated) return@pointerInput if (!state.authenticated) return@pointerInput
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "linkedsign-backend", "name": "linkedsign-backend",
"version": "1.2.4", "version": "1.2.5",
"private": true, "private": true,
"description": "Initial backend for LinkedSign", "description": "Initial backend for LinkedSign",
"main": "src/server.js", "main": "src/server.js",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "frontend", "name": "frontend",
"private": true, "private": true,
"version": "1.2.4", "version": "1.2.5",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",