feat: Add EnableAppLockTile to SettingsScreen

This commit is contained in:
Myzel394 2023-12-23 20:18:46 +01:00
parent a73fc6c48f
commit 025a8a3209
No known key found for this signature in database
GPG Key ID: 50098FCA22080F0F
6 changed files with 124 additions and 14 deletions

View File

@ -22,6 +22,8 @@ data class AppSettings(
val audioRecorderSettings: AudioRecorderSettings = AudioRecorderSettings.getDefaultInstance(),
val videoRecorderSettings: VideoRecorderSettings = VideoRecorderSettings.getDefaultInstance(),
val appLockSettings: AppLockSettings? = null,
val hasSeenOnboarding: Boolean = false,
val showAdvancedSettings: Boolean = false,
val theme: Theme = Theme.SYSTEM,
@ -97,6 +99,14 @@ data class AppSettings(
return copy(saveFolder = saveFolder)
}
fun setAppLockSettings(appLockSettings: AppLockSettings?): AppSettings {
return copy(appLockSettings = appLockSettings)
}
// If the object is present, biometric authentication is enabled.
// To disable biometric authentication, set the instance to null.
fun isAppLockEnabled() = appLockSettings != null
enum class Theme {
SYSTEM,
LIGHT,
@ -553,8 +563,7 @@ data class NotificationSettings(
@Serializable
class AppLockSettings {
// If the object is present, biometric authentication is enabled.
// To disable biometric authentication, set the instance to null.
val isEnabled
get() = true
companion object {
fun getDefaultInstance() = AppLockSettings()
}
}

View File

@ -15,13 +15,13 @@ class AppLockHelper {
}
companion object {
fun isSupported(context: Context): SupportType {
fun getSupportType(context: Context): SupportType {
val biometricManager = BiometricManager.from(context)
when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)) {
BiometricManager.BIOMETRIC_SUCCESS -> return SupportType.AVAILABLE
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> return SupportType.NONE_ENROLLED
return when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)) {
BiometricManager.BIOMETRIC_SUCCESS -> SupportType.AVAILABLE
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> SupportType.NONE_ENROLLED
else -> return SupportType.UNAVAILABLE
else -> SupportType.UNAVAILABLE
}
}
@ -29,23 +29,23 @@ class AppLockHelper {
context: Context,
title: String,
subtitle: String
): CompletableDeferred<Unit> {
val deferred = CompletableDeferred<Unit>()
): CompletableDeferred<Boolean> {
val deferred = CompletableDeferred<Boolean>()
val mainExecutor = ContextCompat.getMainExecutor(context)
val biometricPrompt = BiometricPrompt(
context as FragmentActivity,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
deferred.completeExceptionally(Exception(errString.toString()))
deferred.complete(false)
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
deferred.complete(Unit)
deferred.complete(true)
}
override fun onAuthenticationFailed() {
deferred.completeExceptionally(Exception("Authentication failed"))
deferred.complete(false)
}
}
)

View File

@ -0,0 +1,92 @@
package app.myzel394.alibi.ui.components.SettingsScreen.Tiles
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.magnifier
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Fingerprint
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
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.db.AppLockSettings
import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.helpers.AppLockHelper
import app.myzel394.alibi.ui.components.atoms.SettingsTile
import kotlinx.coroutines.launch
@Composable
fun EnableAppLockTile(
settings: AppSettings,
) {
val scope = rememberCoroutineScope()
val context = LocalContext.current
val dataStore = context.dataStore
val appLockSupport = AppLockHelper.getSupportType(context)
if (appLockSupport === AppLockHelper.SupportType.UNAVAILABLE) {
return
}
SettingsTile(
title = stringResource(R.string.ui_settings_option_enableAppLock_title),
description = stringResource(R.string.ui_settings_option_enableAppLock_description),
tertiaryLine = {
if (appLockSupport === AppLockHelper.SupportType.NONE_ENROLLED) {
Text(
stringResource(R.string.ui_settings_option_enableAppLock_enrollmentRequired),
color = Color.Yellow,
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(top = 4.dp)
)
}
},
leading = {
Icon(
Icons.Default.Fingerprint,
contentDescription = null,
)
},
trailing = {
val title = stringResource(R.string.identityVerificationRequired_title)
val subtitle = stringResource(R.string.identityVerificationRequired_subtitle)
Switch(
checked = settings.isAppLockEnabled(),
enabled = appLockSupport === AppLockHelper.SupportType.AVAILABLE,
onCheckedChange = {
scope.launch {
val authenticationSuccessful = AppLockHelper.authenticate(
context,
title = title,
subtitle = subtitle,
).await()
if (!authenticationSuccessful) {
return@launch
}
dataStore.updateData {
it.setAppLockSettings(
if (it.appLockSettings == null)
AppLockSettings.getDefaultInstance()
else
null
)
}
}
}
)
}
)
}

View File

@ -21,6 +21,7 @@ fun SettingsTile(
firstModifier: Modifier = Modifier,
title: String,
description: String? = null,
tertiaryLine: (@Composable () -> Unit) = {},
leading: @Composable () -> Unit = {},
trailing: @Composable () -> Unit = {},
extra: (@Composable () -> Unit)? = null,
@ -49,6 +50,7 @@ fun SettingsTile(
text = description,
style = MaterialTheme.typography.bodySmall,
)
tertiaryLine()
}
Spacer(modifier = Modifier.width(16.dp))
trailing()

View File

@ -52,6 +52,7 @@ import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderOutput
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderSamplingRateTile
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.SaveFolderTile
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderShowAllMicrophonesTile
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.EnableAppLockTile
import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderBitrateTile
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ThemeSelector
import app.myzel394.alibi.ui.components.atoms.GlobalSwitch
@ -138,6 +139,7 @@ fun SettingsScreen(
InAppLanguagePicker()
DeleteRecordingsImmediatelyTile(settings = settings)
CustomNotificationTile(navController = navController, settings = settings)
EnableAppLockTile(settings = settings)
GlobalSwitch(
label = stringResource(R.string.ui_settings_advancedSettings_label),
checked = settings.showAdvancedSettings,

View File

@ -162,4 +162,9 @@
<string name="ui_videoRecorder_info_starting">Video Recorder is starting...</string>
<string name="ui_locked_title">Alibi is locked</string>
<string name="ui_locked_unlocked">Unlock</string>
<string name="ui_settings_option_enableAppLock_title">Enable App Lock</string>
<string name="ui_settings_option_enableAppLock_description">Require your biometric info or your password to open Alibi</string>
<string name="ui_settings_option_enableAppLock_enrollmentRequired">Please enroll a password or a biometric unlock method first</string>
<string name="identityVerificationRequired_title">Verification required</string>
<string name="identityVerificationRequired_subtitle">You need to verify your identity to continue</string>
</resources>