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
|
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())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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-->
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user