mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 19:15:58 +00:00
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:
@@ -1,55 +1,34 @@
|
||||
package com.bintianqi.owndroid
|
||||
|
||||
import android.content.Context
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.biometric.BiometricPrompt.AuthenticationCallback
|
||||
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.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
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.core.content.ContextCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.bintianqi.owndroid.ui.Animations
|
||||
import androidx.navigation.NavHostController
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun AuthScreen(activity: FragmentActivity, showAuth: MutableState<Boolean>) {
|
||||
val context = activity.applicationContext
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
var canStartAuth by remember { mutableStateOf(true) }
|
||||
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)
|
||||
fun Authenticate(activity: FragmentActivity, navCtrl: NavHostController) {
|
||||
BackHandler { activity.moveTaskToBack(true) }
|
||||
var status by rememberSaveable { mutableIntStateOf(0) } // 0:Prompt automatically, 1:Authenticating, 2:Prompt manually
|
||||
val onAuthSucceed = { navCtrl.navigateUp() }
|
||||
val callback = object: AuthenticationCallback() {
|
||||
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||
super.onAuthenticationSucceeded(result)
|
||||
@@ -59,49 +38,33 @@ fun AuthScreen(activity: FragmentActivity, showAuth: MutableState<Boolean>) {
|
||||
super.onAuthenticationError(errorCode, errString)
|
||||
when(errorCode) {
|
||||
BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL -> onAuthSucceed()
|
||||
BiometricPrompt.ERROR_NEGATIVE_BUTTON -> fallback = true
|
||||
else -> canStartAuth = true
|
||||
else -> status = 2
|
||||
}
|
||||
}
|
||||
}
|
||||
LaunchedEffect(fallback) {
|
||||
if(fallback) {
|
||||
val fallbackPromptInfo = promptInfo
|
||||
.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
|
||||
.setSubtitle(context.getText(R.string.auth_with_password))
|
||||
.build()
|
||||
val executor = ContextCompat.getMainExecutor(context)
|
||||
val biometricPrompt = BiometricPrompt(activity, executor, callback)
|
||||
biometricPrompt.authenticate(fallbackPromptInfo)
|
||||
LaunchedEffect(Unit) {
|
||||
if(status == 0) {
|
||||
delay(300)
|
||||
startAuth(activity, callback)
|
||||
status = 1
|
||||
}
|
||||
}
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.alpha(alpha)
|
||||
.background(if(isSystemInDarkTheme()) Color.Black else Color.White)
|
||||
) {
|
||||
Scaffold { paddingValues ->
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
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 = stringResource(R.string.authenticate),
|
||||
style = MaterialTheme.typography.headlineLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
Button(
|
||||
onClick = {
|
||||
startAuth(activity, promptInfo, callback)
|
||||
canStartAuth = false
|
||||
startAuth(activity, callback)
|
||||
status = 1
|
||||
},
|
||||
enabled = canStartAuth
|
||||
enabled = status != 1
|
||||
) {
|
||||
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 promptInfo = basicPromptInfo
|
||||
val bioManager = BiometricManager.from(context)
|
||||
val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE)
|
||||
if(sharedPref.getBoolean("bio_auth", false)) {
|
||||
when(BiometricManager.BIOMETRIC_SUCCESS) {
|
||||
bioManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG) ->
|
||||
promptInfo
|
||||
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
|
||||
.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 promptInfo = Builder().setTitle(context.getText(R.string.authenticate))
|
||||
if(sharedPref.getInt("biometrics_auth", 0) != 0) {
|
||||
promptInfo.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL or BiometricManager.Authenticators.BIOMETRIC_WEAK)
|
||||
} else {
|
||||
promptInfo.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
|
||||
}
|
||||
val executor = ContextCompat.getMainExecutor(context)
|
||||
val biometricPrompt = BiometricPrompt(activity, executor, callback)
|
||||
biometricPrompt.authenticate(promptInfo.build())
|
||||
BiometricPrompt(activity, executor, callback).authenticate(promptInfo.build())
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ import android.widget.Toast
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
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.clickable
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
@@ -30,6 +33,7 @@ import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -48,6 +52,9 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavHostController
|
||||
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.LockTaskMode
|
||||
import com.bintianqi.owndroid.dpm.MTEPolicy
|
||||
import com.bintianqi.owndroid.dpm.WorkProfile
|
||||
import com.bintianqi.owndroid.dpm.NearbyStreamingPolicy
|
||||
import com.bintianqi.owndroid.dpm.Network
|
||||
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.WifiSsidPolicy
|
||||
import com.bintianqi.owndroid.dpm.WipeData
|
||||
import com.bintianqi.owndroid.dpm.WorkProfile
|
||||
import com.bintianqi.owndroid.dpm.dhizukuErrorStatus
|
||||
import com.bintianqi.owndroid.dpm.dhizukuPermissionGranted
|
||||
import com.bintianqi.owndroid.dpm.getDPM
|
||||
@@ -134,20 +141,16 @@ import kotlinx.coroutines.launch
|
||||
import org.lsposed.hiddenapibypass.HiddenApiBypass
|
||||
import java.util.Locale
|
||||
|
||||
var backToHomeStateFlow = MutableStateFlow(false)
|
||||
val backToHomeStateFlow = MutableStateFlow(false)
|
||||
@ExperimentalMaterial3Api
|
||||
class MainActivity : FragmentActivity() {
|
||||
private val showAuth = mutableStateOf(false)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
registerActivityResult(this)
|
||||
enableEdgeToEdge()
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
super.onCreate(savedInstanceState)
|
||||
val context = applicationContext
|
||||
val sharedPref = context.getSharedPreferences("data", MODE_PRIVATE)
|
||||
if (VERSION.SDK_INT >= 28) HiddenApiBypass.setHiddenApiExemptions("")
|
||||
if(sharedPref.getBoolean("auth", false)) showAuth.value = true
|
||||
val locale = context.resources?.configuration?.locale
|
||||
zhCN = locale == Locale.SIMPLIFIED_CHINESE || locale == Locale.CHINESE || locale == Locale.CHINA
|
||||
toggleInstallAppActivity()
|
||||
@@ -156,10 +159,7 @@ class MainActivity : FragmentActivity() {
|
||||
lifecycleScope.launch { delay(5000); setDefaultAffiliationID(context) }
|
||||
setContent {
|
||||
OwnDroidTheme(vm) {
|
||||
Home(vm)
|
||||
if(showAuth.value) {
|
||||
AuthScreen(this, showAuth)
|
||||
}
|
||||
Home(this, vm)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,12 +167,6 @@ class MainActivity : FragmentActivity() {
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
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 (Dhizuku.init(applicationContext)) {
|
||||
if (!dhizukuPermissionGranted()) { dhizukuErrorStatus.value = 2 }
|
||||
@@ -187,7 +181,7 @@ class MainActivity : FragmentActivity() {
|
||||
|
||||
@ExperimentalMaterial3Api
|
||||
@Composable
|
||||
fun Home(vm: MyViewModel) {
|
||||
fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
val navCtrl = rememberNavController()
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
@@ -196,6 +190,7 @@ fun Home(vm: MyViewModel) {
|
||||
val focusMgr = LocalFocusManager.current
|
||||
val dialogStatus = remember { mutableIntStateOf(0) }
|
||||
val backToHome by backToHomeStateFlow.collectAsState()
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
LaunchedEffect(backToHome) {
|
||||
if(backToHome) { navCtrl.navigateUp(); backToHomeStateFlow.value = false }
|
||||
}
|
||||
@@ -308,6 +303,28 @@ fun Home(vm: MyViewModel) {
|
||||
composable(route = "About") { About(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) {
|
||||
val profileInitialized = sharedPref.getBoolean("ManagedProfileActivated", false)
|
||||
|
||||
@@ -5,9 +5,15 @@ import android.os.Bundle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.biometric.BiometricPrompt.AuthenticationCallback
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Text
|
||||
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.core.view.WindowCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
@@ -20,17 +26,40 @@ class ManageSpaceActivity: FragmentActivity() {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
super.onCreate(savedInstanceState)
|
||||
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>()
|
||||
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 {
|
||||
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) {
|
||||
AlertDialog(
|
||||
title = {
|
||||
Text(stringResource(R.string.clear_storage))
|
||||
},
|
||||
text = {
|
||||
if(protected) Text(stringResource(R.string.storage_is_protected))
|
||||
Text(stringResource(R.string.clear_storage))
|
||||
},
|
||||
onDismissRequest = { finish() },
|
||||
dismissButton = {
|
||||
@@ -39,19 +68,16 @@ class ManageSpaceActivity: FragmentActivity() {
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
if(!protected) TextButton(
|
||||
TextButton(
|
||||
onClick = {
|
||||
filesDir.deleteRecursively()
|
||||
cacheDir.deleteRecursively()
|
||||
codeCacheDir.deleteRecursively()
|
||||
if(Build.VERSION.SDK_INT >= 24) {
|
||||
dataDir.resolve("shared_prefs").deleteRecursively()
|
||||
if(authenticate) {
|
||||
authenticating = true
|
||||
startAuth(this, callback)
|
||||
} else {
|
||||
sharedPref.edit().clear().apply()
|
||||
clearStorage()
|
||||
}
|
||||
finish()
|
||||
exitProcess(0)
|
||||
}
|
||||
},
|
||||
enabled = !authenticating
|
||||
) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build.VERSION
|
||||
import android.widget.Toast
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -19,7 +20,9 @@ import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -117,7 +120,8 @@ fun Appearance(navCtrl: NavHostController, vm: MyViewModel) {
|
||||
|
||||
@Composable
|
||||
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)) }
|
||||
MyScaffold(R.string.security, 0.dp, navCtrl) {
|
||||
SwitchItem(
|
||||
@@ -128,22 +132,24 @@ fun AuthSettings(navCtrl: NavHostController) {
|
||||
}
|
||||
)
|
||||
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(
|
||||
R.string.enable_bio_auth, "", null,
|
||||
{ sharedPref.getBoolean("bio_auth", false) },
|
||||
{ sharedPref.edit().putBoolean("bio_auth", it).apply() }
|
||||
R.string.enable_bio_auth, "", null, bioAuth != 0,
|
||||
{ bioAuth = if(it) 1 else 0; sharedPref.edit().putInt("biometrics_auth", bioAuth).apply() }, bioAuth != 2
|
||||
)
|
||||
SwitchItem(
|
||||
R.string.lock_in_background, stringResource(R.string.developing), null,
|
||||
R.string.lock_in_background, "", null,
|
||||
{ sharedPref.getBoolean("lock_in_background", false) },
|
||||
{ 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() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,6 @@ object Animations {
|
||||
|
||||
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 = {
|
||||
fadeIn(tween(100, easing = LinearEasing)) +
|
||||
slideIntoContainer(
|
||||
|
||||
Reference in New Issue
Block a user