Fix some bugs of authentication

Display authentication screen in NavHost
Remove "Protect storage", authenticate to clear storage instead
Force enable biometrics on if using password alone is not supported
This commit is contained in:
BinTianqi
2024-12-14 22:03:39 +08:00
parent f7b18d1a31
commit 867668832e
10 changed files with 120 additions and 146 deletions

View File

@@ -1,55 +1,34 @@
package com.bintianqi.owndroid package com.bintianqi.owndroid
import android.content.Context import android.content.Context
import androidx.activity.compose.BackHandler
import androidx.biometric.BiometricManager import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt import androidx.biometric.BiometricPrompt
import androidx.biometric.BiometricPrompt.AuthenticationCallback import androidx.biometric.BiometricPrompt.AuthenticationCallback
import androidx.biometric.BiometricPrompt.PromptInfo.Builder import androidx.biometric.BiometricPrompt.PromptInfo.Builder
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import com.bintianqi.owndroid.ui.Animations import androidx.navigation.NavHostController
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Composable @Composable
fun AuthScreen(activity: FragmentActivity, showAuth: MutableState<Boolean>) { fun Authenticate(activity: FragmentActivity, navCtrl: NavHostController) {
val context = activity.applicationContext BackHandler { activity.moveTaskToBack(true) }
val coroutineScope = rememberCoroutineScope() var status by rememberSaveable { mutableIntStateOf(0) } // 0:Prompt automatically, 1:Authenticating, 2:Prompt manually
var canStartAuth by remember { mutableStateOf(true) } val onAuthSucceed = { navCtrl.navigateUp() }
var fallback by remember { mutableStateOf(false) }
var startFade by remember { mutableStateOf(false) }
val alpha by animateFloatAsState(
targetValue = if(startFade) 0F else 1F,
label = "AuthScreenFade",
animationSpec = Animations.authScreenFade
)
val onAuthSucceed = {
startFade = true
coroutineScope.launch {
delay(300)
showAuth.value = false
}
}
val promptInfo = Builder()
.setTitle(context.getText(R.string.authenticate))
.setSubtitle(context.getText(R.string.auth_with_bio))
.setConfirmationRequired(true)
val callback = object: AuthenticationCallback() { val callback = object: AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result) super.onAuthenticationSucceeded(result)
@@ -59,49 +38,33 @@ fun AuthScreen(activity: FragmentActivity, showAuth: MutableState<Boolean>) {
super.onAuthenticationError(errorCode, errString) super.onAuthenticationError(errorCode, errString)
when(errorCode) { when(errorCode) {
BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL -> onAuthSucceed() BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL -> onAuthSucceed()
BiometricPrompt.ERROR_NEGATIVE_BUTTON -> fallback = true else -> status = 2
else -> canStartAuth = true
} }
} }
} }
LaunchedEffect(fallback) { LaunchedEffect(Unit) {
if(fallback) { if(status == 0) {
val fallbackPromptInfo = promptInfo delay(300)
.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL) startAuth(activity, callback)
.setSubtitle(context.getText(R.string.auth_with_password)) status = 1
.build()
val executor = ContextCompat.getMainExecutor(context)
val biometricPrompt = BiometricPrompt(activity, executor, callback)
biometricPrompt.authenticate(fallbackPromptInfo)
} }
} }
Surface( Scaffold { paddingValues ->
modifier = Modifier
.fillMaxSize()
.alpha(alpha)
.background(if(isSystemInDarkTheme()) Color.Black else Color.White)
) {
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background) modifier = Modifier.fillMaxSize().padding(paddingValues)
) { ) {
LaunchedEffect(Unit) {
delay(300)
startAuth(activity, promptInfo, callback)
canStartAuth = false
}
Text( Text(
text = stringResource(R.string.authenticate), text = stringResource(R.string.authenticate),
style = MaterialTheme.typography.headlineLarge, style = MaterialTheme.typography.headlineLarge,
color = MaterialTheme.colorScheme.onBackground
) )
Button( Button(
onClick = { onClick = {
startAuth(activity, promptInfo, callback) startAuth(activity, callback)
canStartAuth = false status = 1
}, },
enabled = canStartAuth enabled = status != 1
) { ) {
Text(text = stringResource(R.string.start)) Text(text = stringResource(R.string.start))
} }
@@ -109,31 +72,15 @@ fun AuthScreen(activity: FragmentActivity, showAuth: MutableState<Boolean>) {
} }
} }
private fun startAuth(activity: FragmentActivity, basicPromptInfo: Builder, callback: AuthenticationCallback) { fun startAuth(activity: FragmentActivity, callback: AuthenticationCallback) {
val context = activity.applicationContext val context = activity.applicationContext
val promptInfo = basicPromptInfo
val bioManager = BiometricManager.from(context)
val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE) val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE)
if(sharedPref.getBoolean("bio_auth", false)) { val promptInfo = Builder().setTitle(context.getText(R.string.authenticate))
when(BiometricManager.BIOMETRIC_SUCCESS) { if(sharedPref.getInt("biometrics_auth", 0) != 0) {
bioManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG) -> promptInfo.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL or BiometricManager.Authenticators.BIOMETRIC_WEAK)
promptInfo } else {
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG) promptInfo.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
.setNegativeButtonText(context.getText(R.string.use_password))
bioManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) ->
promptInfo
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_WEAK)
.setNegativeButtonText(context.getText(R.string.use_password))
else -> promptInfo
.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
.setSubtitle(context.getText(R.string.auth_with_password))
}
}else{
promptInfo
.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
.setSubtitle(context.getText(R.string.auth_with_password))
} }
val executor = ContextCompat.getMainExecutor(context) val executor = ContextCompat.getMainExecutor(context)
val biometricPrompt = BiometricPrompt(activity, executor, callback) BiometricPrompt(activity, executor, callback).authenticate(promptInfo.build())
biometricPrompt.authenticate(promptInfo.build())
} }

View File

@@ -8,6 +8,9 @@ import android.widget.Toast
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectTapGestures
@@ -30,6 +33,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
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.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -48,6 +52,9 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
@@ -77,7 +84,6 @@ import com.bintianqi.owndroid.dpm.Keyguard
import com.bintianqi.owndroid.dpm.LockScreenInfo import com.bintianqi.owndroid.dpm.LockScreenInfo
import com.bintianqi.owndroid.dpm.LockTaskMode import com.bintianqi.owndroid.dpm.LockTaskMode
import com.bintianqi.owndroid.dpm.MTEPolicy import com.bintianqi.owndroid.dpm.MTEPolicy
import com.bintianqi.owndroid.dpm.WorkProfile
import com.bintianqi.owndroid.dpm.NearbyStreamingPolicy import com.bintianqi.owndroid.dpm.NearbyStreamingPolicy
import com.bintianqi.owndroid.dpm.Network import com.bintianqi.owndroid.dpm.Network
import com.bintianqi.owndroid.dpm.NetworkLogging import com.bintianqi.owndroid.dpm.NetworkLogging
@@ -115,6 +121,7 @@ import com.bintianqi.owndroid.dpm.WifiAuthKeypair
import com.bintianqi.owndroid.dpm.WifiSecurityLevel import com.bintianqi.owndroid.dpm.WifiSecurityLevel
import com.bintianqi.owndroid.dpm.WifiSsidPolicy import com.bintianqi.owndroid.dpm.WifiSsidPolicy
import com.bintianqi.owndroid.dpm.WipeData import com.bintianqi.owndroid.dpm.WipeData
import com.bintianqi.owndroid.dpm.WorkProfile
import com.bintianqi.owndroid.dpm.dhizukuErrorStatus import com.bintianqi.owndroid.dpm.dhizukuErrorStatus
import com.bintianqi.owndroid.dpm.dhizukuPermissionGranted import com.bintianqi.owndroid.dpm.dhizukuPermissionGranted
import com.bintianqi.owndroid.dpm.getDPM import com.bintianqi.owndroid.dpm.getDPM
@@ -134,20 +141,16 @@ import kotlinx.coroutines.launch
import org.lsposed.hiddenapibypass.HiddenApiBypass import org.lsposed.hiddenapibypass.HiddenApiBypass
import java.util.Locale import java.util.Locale
var backToHomeStateFlow = MutableStateFlow(false) val backToHomeStateFlow = MutableStateFlow(false)
@ExperimentalMaterial3Api @ExperimentalMaterial3Api
class MainActivity : FragmentActivity() { class MainActivity : FragmentActivity() {
private val showAuth = mutableStateOf(false)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
registerActivityResult(this) registerActivityResult(this)
enableEdgeToEdge() enableEdgeToEdge()
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val context = applicationContext val context = applicationContext
val sharedPref = context.getSharedPreferences("data", MODE_PRIVATE)
if (VERSION.SDK_INT >= 28) HiddenApiBypass.setHiddenApiExemptions("") if (VERSION.SDK_INT >= 28) HiddenApiBypass.setHiddenApiExemptions("")
if(sharedPref.getBoolean("auth", false)) showAuth.value = true
val locale = context.resources?.configuration?.locale val locale = context.resources?.configuration?.locale
zhCN = locale == Locale.SIMPLIFIED_CHINESE || locale == Locale.CHINESE || locale == Locale.CHINA zhCN = locale == Locale.SIMPLIFIED_CHINESE || locale == Locale.CHINESE || locale == Locale.CHINA
toggleInstallAppActivity() toggleInstallAppActivity()
@@ -156,10 +159,7 @@ class MainActivity : FragmentActivity() {
lifecycleScope.launch { delay(5000); setDefaultAffiliationID(context) } lifecycleScope.launch { delay(5000); setDefaultAffiliationID(context) }
setContent { setContent {
OwnDroidTheme(vm) { OwnDroidTheme(vm) {
Home(vm) Home(this, vm)
if(showAuth.value) {
AuthScreen(this, showAuth)
}
} }
} }
} }
@@ -167,12 +167,6 @@ class MainActivity : FragmentActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val sharedPref = applicationContext.getSharedPreferences("data", MODE_PRIVATE) val sharedPref = applicationContext.getSharedPreferences("data", MODE_PRIVATE)
if(
sharedPref.getBoolean("auth", false) &&
sharedPref.getBoolean("lock_in_background", false)
) {
showAuth.value = true
}
if (sharedPref.getBoolean("dhizuku", false)) { if (sharedPref.getBoolean("dhizuku", false)) {
if (Dhizuku.init(applicationContext)) { if (Dhizuku.init(applicationContext)) {
if (!dhizukuPermissionGranted()) { dhizukuErrorStatus.value = 2 } if (!dhizukuPermissionGranted()) { dhizukuErrorStatus.value = 2 }
@@ -187,7 +181,7 @@ class MainActivity : FragmentActivity() {
@ExperimentalMaterial3Api @ExperimentalMaterial3Api
@Composable @Composable
fun Home(vm: MyViewModel) { fun Home(activity: FragmentActivity, vm: MyViewModel) {
val navCtrl = rememberNavController() val navCtrl = rememberNavController()
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
@@ -196,6 +190,7 @@ fun Home(vm: MyViewModel) {
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
val dialogStatus = remember { mutableIntStateOf(0) } val dialogStatus = remember { mutableIntStateOf(0) }
val backToHome by backToHomeStateFlow.collectAsState() val backToHome by backToHomeStateFlow.collectAsState()
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(backToHome) { LaunchedEffect(backToHome) {
if(backToHome) { navCtrl.navigateUp(); backToHomeStateFlow.value = false } if(backToHome) { navCtrl.navigateUp(); backToHomeStateFlow.value = false }
} }
@@ -308,6 +303,28 @@ fun Home(vm: MyViewModel) {
composable(route = "About") { About(navCtrl) } composable(route = "About") { About(navCtrl) }
composable(route = "PackageSelector") { PackageSelector(navCtrl) } composable(route = "PackageSelector") { PackageSelector(navCtrl) }
composable(
route = "Authenticate",
enterTransition = { fadeIn(animationSpec = tween(200)) },
popExitTransition = { fadeOut(animationSpec = tween(400)) }
) { Authenticate(activity, navCtrl) }
}
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if(
(event == Lifecycle.Event.ON_RESUME &&
sharedPref.getBoolean("auth", false) &&
sharedPref.getBoolean("lock_in_background", false)) ||
(event == Lifecycle.Event.ON_CREATE && sharedPref.getBoolean("auth", false))
) {
navCtrl.navigate("Authenticate") { launchSingleTop = true }
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
} }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
val profileInitialized = sharedPref.getBoolean("ManagedProfileActivated", false) val profileInitialized = sharedPref.getBoolean("ManagedProfileActivated", false)

View File

@@ -5,9 +5,15 @@ import android.os.Bundle
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.biometric.BiometricPrompt
import androidx.biometric.BiometricPrompt.AuthenticationCallback
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
@@ -20,17 +26,40 @@ class ManageSpaceActivity: FragmentActivity() {
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val sharedPref = applicationContext.getSharedPreferences("data", MODE_PRIVATE) val sharedPref = applicationContext.getSharedPreferences("data", MODE_PRIVATE)
val protected = sharedPref.getBoolean("protect_storage", false) val authenticate = sharedPref.getBoolean("auth", false)
val vm by viewModels<MyViewModel>() val vm by viewModels<MyViewModel>()
if(!vm.initialized) vm.initialize(applicationContext) if(!vm.initialized) vm.initialize(applicationContext)
fun clearStorage() {
filesDir.deleteRecursively()
cacheDir.deleteRecursively()
codeCacheDir.deleteRecursively()
if(Build.VERSION.SDK_INT >= 24) {
dataDir.resolve("shared_prefs").deleteRecursively()
} else {
sharedPref.edit().clear().apply()
}
finish()
exitProcess(0)
}
setContent { setContent {
var authenticating by remember { mutableStateOf(false) }
val callback = object: AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
clearStorage()
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
when(errorCode) {
BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL -> clearStorage()
else -> authenticating = false
}
}
}
OwnDroidTheme(vm) { OwnDroidTheme(vm) {
AlertDialog( AlertDialog(
title = {
Text(stringResource(R.string.clear_storage))
},
text = { text = {
if(protected) Text(stringResource(R.string.storage_is_protected)) Text(stringResource(R.string.clear_storage))
}, },
onDismissRequest = { finish() }, onDismissRequest = { finish() },
dismissButton = { dismissButton = {
@@ -39,19 +68,16 @@ class ManageSpaceActivity: FragmentActivity() {
} }
}, },
confirmButton = { confirmButton = {
if(!protected) TextButton( TextButton(
onClick = { onClick = {
filesDir.deleteRecursively() if(authenticate) {
cacheDir.deleteRecursively() authenticating = true
codeCacheDir.deleteRecursively() startAuth(this, callback)
if(Build.VERSION.SDK_INT >= 24) {
dataDir.resolve("shared_prefs").deleteRecursively()
} else { } else {
sharedPref.edit().clear().apply() clearStorage()
} }
finish() },
exitProcess(0) enabled = !authenticating
}
) { ) {
Text(stringResource(R.string.confirm)) Text(stringResource(R.string.confirm))
} }

View File

@@ -5,6 +5,7 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build.VERSION import android.os.Build.VERSION
import android.widget.Toast import android.widget.Toast
import androidx.biometric.BiometricManager
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -19,7 +20,9 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
@@ -117,7 +120,8 @@ fun Appearance(navCtrl: NavHostController, vm: MyViewModel) {
@Composable @Composable
fun AuthSettings(navCtrl: NavHostController) { fun AuthSettings(navCtrl: NavHostController) {
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE) val context = LocalContext.current
val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE)
var auth by remember{ mutableStateOf(sharedPref.getBoolean("auth",false)) } var auth by remember{ mutableStateOf(sharedPref.getBoolean("auth",false)) }
MyScaffold(R.string.security, 0.dp, navCtrl) { MyScaffold(R.string.security, 0.dp, navCtrl) {
SwitchItem( SwitchItem(
@@ -128,22 +132,24 @@ fun AuthSettings(navCtrl: NavHostController) {
} }
) )
if(auth) { if(auth) {
var bioAuth by remember { mutableIntStateOf(sharedPref.getInt("biometrics_auth", 0)) } // 0:Disabled, 1:Enabled 2:Force enabled
LaunchedEffect(Unit) {
val bioManager = BiometricManager.from(context)
if(bioManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL) != BiometricManager.BIOMETRIC_SUCCESS) {
bioAuth = 2
sharedPref.edit().putInt("biometrics_auth", 2).apply()
}
}
SwitchItem( SwitchItem(
R.string.enable_bio_auth, "", null, R.string.enable_bio_auth, "", null, bioAuth != 0,
{ sharedPref.getBoolean("bio_auth", false) }, { bioAuth = if(it) 1 else 0; sharedPref.edit().putInt("biometrics_auth", bioAuth).apply() }, bioAuth != 2
{ sharedPref.edit().putBoolean("bio_auth", it).apply() }
) )
SwitchItem( SwitchItem(
R.string.lock_in_background, stringResource(R.string.developing), null, R.string.lock_in_background, "", null,
{ sharedPref.getBoolean("lock_in_background", false) }, { sharedPref.getBoolean("lock_in_background", false) },
{ sharedPref.edit().putBoolean("lock_in_background", it).apply() } { sharedPref.edit().putBoolean("lock_in_background", it).apply() }
) )
} }
SwitchItem(
R.string.protect_storage, "", null,
{ sharedPref.getBoolean("protect_storage", false) },
{ sharedPref.edit().putBoolean("protect_storage", it).apply() }
)
} }
} }

View File

@@ -13,8 +13,6 @@ object Animations {
private val tween: FiniteAnimationSpec<IntOffset> = tween(durationMillis = 550, easing = bezier, delayMillis = 50) private val tween: FiniteAnimationSpec<IntOffset> = tween(durationMillis = 550, easing = bezier, delayMillis = 50)
val authScreenFade: FiniteAnimationSpec<Float> = tween(durationMillis = 200, easing = LinearEasing)
val navHostEnterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = { val navHostEnterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = {
fadeIn(tween(100, easing = LinearEasing)) + fadeIn(tween(100, easing = LinearEasing)) +
slideIntoContainer( slideIntoContainer(

View File

@@ -557,12 +557,7 @@
<string name="lock_owndroid">Заблокировать OwnDroid</string> <string name="lock_owndroid">Заблокировать OwnDroid</string>
<string name="enable_bio_auth">Аутентификация по биометрии</string> <string name="enable_bio_auth">Аутентификация по биометрии</string>
<string name="authenticate">Аутентифицировать</string> <string name="authenticate">Аутентифицировать</string>
<string name="use_password">Использовать пароль</string>
<string name="auth_with_password">Аутентифицировать OwnDroid с помощью пароля</string>
<string name="auth_with_bio">Аутентифицировать OwnDroid с помощью биометрии</string>
<string name="lock_in_background">Блокировать при переключении в фоновый режим</string> <string name="lock_in_background">Блокировать при переключении в фоновый режим</string>
<string name="protect_storage">Защитить хранилище</string>
<string name="storage_is_protected">Хранилище защищено, вы не можете очистить хранилище OwnDroid</string>
<string name="clear_storage">Очистить хранилище</string> <string name="clear_storage">Очистить хранилище</string>
<string name="automation_api">API автоматизации</string> <string name="automation_api">API автоматизации</string>

View File

@@ -552,12 +552,7 @@
<string name="lock_owndroid">OwnDroid\'u kilitle</string> <string name="lock_owndroid">OwnDroid\'u kilitle</string>
<string name="enable_bio_auth">Biyometri ile doğrulama</string> <string name="enable_bio_auth">Biyometri ile doğrulama</string>
<string name="authenticate">Doğrula</string> <string name="authenticate">Doğrula</string>
<string name="use_password">Şifre kullan</string>
<string name="auth_with_password">OwnDroid\'u şifre ile doğrula</string>
<string name="auth_with_bio">OwnDroid\'u biyometri ile doğrula</string>
<string name="lock_in_background">Arka plana geçince kilitle</string> <string name="lock_in_background">Arka plana geçince kilitle</string>
<string name="protect_storage">Depolamayı koru</string>
<string name="storage_is_protected">Depolama korunuyor</string> <!--TODO-->
<string name="clear_storage">Depolamayı temizle</string> <string name="clear_storage">Depolamayı temizle</string>
<string name="automation_api">Automation API</string> <!--TODO--> <string name="automation_api">Automation API</string> <!--TODO-->

View File

@@ -543,12 +543,7 @@
<string name="lock_owndroid">锁定OwnDroid</string> <string name="lock_owndroid">锁定OwnDroid</string>
<string name="enable_bio_auth">使用生物识别</string> <string name="enable_bio_auth">使用生物识别</string>
<string name="authenticate">验证</string> <string name="authenticate">验证</string>
<string name="use_password">使用密码</string>
<string name="auth_with_password">使用密码进行验证</string>
<string name="auth_with_bio">使用生物识别进行验证</string>
<string name="lock_in_background">处于后台时锁定</string> <string name="lock_in_background">处于后台时锁定</string>
<string name="protect_storage">保护存储空间</string>
<string name="storage_is_protected">存储空间受到保护你不能清除OwnDroid的存储空间</string>
<string name="clear_storage">清除存储空间</string> <string name="clear_storage">清除存储空间</string>
<string name="automation_api">自动化API</string> <string name="automation_api">自动化API</string>

View File

@@ -557,12 +557,7 @@
<string name="lock_owndroid">Lock OwnDroid</string> <string name="lock_owndroid">Lock OwnDroid</string>
<string name="enable_bio_auth">Auth with biometrics</string> <string name="enable_bio_auth">Auth with biometrics</string>
<string name="authenticate">Authenticate</string> <string name="authenticate">Authenticate</string>
<string name="use_password">Use password</string>
<string name="auth_with_password">Authenticate OwnDroid with password</string>
<string name="auth_with_bio">Authenticate OwnDroid with biometrics</string>
<string name="lock_in_background">Lock when switch to background</string> <string name="lock_in_background">Lock when switch to background</string>
<string name="protect_storage">Protect storage</string>
<string name="storage_is_protected">Storage is protected, you can\'t clear storage of OwnDroid</string>
<string name="clear_storage">Clear storage</string> <string name="clear_storage">Clear storage</string>
<string name="automation_api">Automation API</string> <string name="automation_api">Automation API</string>

View File

@@ -2,8 +2,8 @@
agp = "8.7.3" agp = "8.7.3"
kotlin = "2.0.21" kotlin = "2.0.21"
navigation-compose = "2.8.4" navigation-compose = "2.8.5"
composeBom = "2024.11.00" composeBom = "2024.12.01"
accompanist-drawablepainter = "0.35.0-alpha" accompanist-drawablepainter = "0.35.0-alpha"
shizuku = "13.1.5" shizuku = "13.1.5"
biometric = "1.2.0-alpha05" biometric = "1.2.0-alpha05"