Use main NavHost to navigate across all pages

Upgrade dependencies
Upload dependencies to GitHub in workflow
This commit is contained in:
BinTianqi
2024-12-07 12:31:13 +08:00
parent 0c670e9265
commit 380675cf8f
19 changed files with 656 additions and 1109 deletions

View File

@@ -18,7 +18,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
@@ -27,6 +26,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@@ -53,15 +53,70 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.dpm.AffiliationID
import com.bintianqi.owndroid.dpm.AlwaysOnVPNPackage
import com.bintianqi.owndroid.dpm.ApplicationManage
import com.bintianqi.owndroid.dpm.DpmPermissions
import com.bintianqi.owndroid.dpm.ManagedProfile
import com.bintianqi.owndroid.dpm.CACert
import com.bintianqi.owndroid.dpm.ChangeTime
import com.bintianqi.owndroid.dpm.ChangeTimeZone
import com.bintianqi.owndroid.dpm.ChangeUserIcon
import com.bintianqi.owndroid.dpm.ChangeUsername
import com.bintianqi.owndroid.dpm.CreateUser
import com.bintianqi.owndroid.dpm.CreateWorkProfile
import com.bintianqi.owndroid.dpm.CurrentUserInfo
import com.bintianqi.owndroid.dpm.DeleteWorkProfile
import com.bintianqi.owndroid.dpm.DeviceAdmin
import com.bintianqi.owndroid.dpm.DeviceInfo
import com.bintianqi.owndroid.dpm.DeviceOwner
import com.bintianqi.owndroid.dpm.DisableAccountManagement
import com.bintianqi.owndroid.dpm.DisableKeyguardFeatures
import com.bintianqi.owndroid.dpm.FRPPolicy
import com.bintianqi.owndroid.dpm.InstallSystemUpdate
import com.bintianqi.owndroid.dpm.IntentFilter
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
import com.bintianqi.owndroid.dpm.NetworkOptions
import com.bintianqi.owndroid.dpm.OrgOwnedProfile
import com.bintianqi.owndroid.dpm.OverrideAPN
import com.bintianqi.owndroid.dpm.Password
import com.bintianqi.owndroid.dpm.PasswordComplexity
import com.bintianqi.owndroid.dpm.PasswordInfo
import com.bintianqi.owndroid.dpm.PasswordQuality
import com.bintianqi.owndroid.dpm.PermissionPolicy
import com.bintianqi.owndroid.dpm.Permissions
import com.bintianqi.owndroid.dpm.PreferentialNetworkService
import com.bintianqi.owndroid.dpm.PrivateDNS
import com.bintianqi.owndroid.dpm.ProfileOwner
import com.bintianqi.owndroid.dpm.RecommendedGlobalProxy
import com.bintianqi.owndroid.dpm.ResetPassword
import com.bintianqi.owndroid.dpm.ResetPasswordToken
import com.bintianqi.owndroid.dpm.RestrictionData
import com.bintianqi.owndroid.dpm.SecurityLogging
import com.bintianqi.owndroid.dpm.Shizuku
import com.bintianqi.owndroid.dpm.SupportMessages
import com.bintianqi.owndroid.dpm.SuspendPersonalApp
import com.bintianqi.owndroid.dpm.SystemManage
import com.bintianqi.owndroid.dpm.UserManage
import com.bintianqi.owndroid.dpm.SystemOptions
import com.bintianqi.owndroid.dpm.SystemUpdatePolicy
import com.bintianqi.owndroid.dpm.TransferOwnership
import com.bintianqi.owndroid.dpm.UserOperation
import com.bintianqi.owndroid.dpm.UserOptions
import com.bintianqi.owndroid.dpm.UserRestriction
import com.bintianqi.owndroid.dpm.UserRestrictionItem
import com.bintianqi.owndroid.dpm.UserSessionMessage
import com.bintianqi.owndroid.dpm.Users
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.dhizukuErrorStatus
import com.bintianqi.owndroid.dpm.dhizukuPermissionGranted
import com.bintianqi.owndroid.dpm.getDPM
import com.bintianqi.owndroid.dpm.getReceiver
import com.bintianqi.owndroid.dpm.isDeviceAdmin
@@ -70,6 +125,7 @@ import com.bintianqi.owndroid.dpm.isProfileOwner
import com.bintianqi.owndroid.dpm.setDefaultAffiliationID
import com.bintianqi.owndroid.dpm.toggleInstallAppActivity
import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
import com.rosan.dhizuku.api.Dhizuku
import kotlinx.coroutines.delay
@@ -119,7 +175,7 @@ class MainActivity : FragmentActivity() {
}
if (sharedPref.getBoolean("dhizuku", false)) {
if (Dhizuku.init(applicationContext)) {
if (!Dhizuku.isPermissionGranted()) { dhizukuErrorStatus.value = 2 }
if (!dhizukuPermissionGranted()) { dhizukuErrorStatus.value = 2 }
} else {
sharedPref.edit().putBoolean("dhizuku", false).apply()
dhizukuErrorStatus.value = 1
@@ -157,20 +213,105 @@ fun Home(vm: MyViewModel) {
popExitTransition = Animations.navHostPopExitTransition
) {
composable(route = "HomePage") { HomePage(navCtrl) }
composable(route = "Permissions") { Permissions(navCtrl) }
composable(route = "Shizuku") { Shizuku(navCtrl) }
composable(route = "DeviceAdmin") { DeviceAdmin(navCtrl) }
composable(route = "ProfileOwner") { ProfileOwner(navCtrl) }
composable(route = "DeviceOwner") { DeviceOwner(navCtrl) }
composable(route = "DeviceInfo") { DeviceInfo(navCtrl) }
composable(route = "LockScreenInfo") { LockScreenInfo(navCtrl) }
composable(route = "SupportMessages") { SupportMessages(navCtrl) }
composable(route = "TransferOwnership") { TransferOwnership(navCtrl) }
composable(route = "System") { SystemManage(navCtrl) }
composable(route = "ManagedProfile") { ManagedProfile(navCtrl) }
composable(route = "Permissions") { DpmPermissions(navCtrl) }
composable(route = "Applications") { ApplicationManage(navCtrl, dialogStatus) }
composable(route = "UserRestriction") { UserRestriction(navCtrl) }
composable(route = "Users") { UserManage(navCtrl) }
composable(route = "Password") { Password(navCtrl) }
composable(route = "Settings") { AppSetting(navCtrl, vm) }
composable(route = "SystemOptions") { SystemOptions(navCtrl) }
composable(route = "Keyguard") { Keyguard(navCtrl) }
composable(route = "ChangeTime") { ChangeTime(navCtrl) }
composable(route = "ChangeTimeZone") { ChangeTimeZone(navCtrl) }
composable(route = "PermissionPolicy") { PermissionPolicy(navCtrl) }
composable(route = "MTEPolicy") { MTEPolicy(navCtrl) }
composable(route = "NearbyStreamingPolicy") { NearbyStreamingPolicy(navCtrl) }
composable(route = "LockTaskMode") { LockTaskMode(navCtrl) }
composable(route = "CACert") { CACert(navCtrl) }
composable(route = "SecurityLogs") { SecurityLogging(navCtrl) }
composable(route = "DisableAccountManagement") { DisableAccountManagement(navCtrl) }
composable(route = "SystemUpdatePolicy") { SystemUpdatePolicy(navCtrl) }
composable(route = "InstallSystemUpdate") { InstallSystemUpdate(navCtrl) }
composable(route = "FRPPolicy") { FRPPolicy(navCtrl) }
composable(route = "WipeData") { WipeData(navCtrl) }
composable(route = "Network") { Network(navCtrl) }
composable(route = "NetworkOptions") { NetworkOptions(navCtrl) }
composable(route = "MinWifiSecurityLevel") { WifiSecurityLevel(navCtrl) }
composable(route = "WifiSsidPolicy") { WifiSsidPolicy(navCtrl) }
composable(route = "PrivateDNS") { PrivateDNS(navCtrl) }
composable(route = "AlwaysOnVpn") { AlwaysOnVPNPackage(navCtrl) }
composable(route = "RecommendedGlobalProxy") { RecommendedGlobalProxy(navCtrl) }
composable(route = "NetworkLog") { NetworkLogging(navCtrl) }
composable(route = "WifiAuthKeypair") { WifiAuthKeypair(navCtrl) }
composable(route = "PreferentialNetworkService") { PreferentialNetworkService(navCtrl) }
composable(route = "OverrideAPN") { OverrideAPN(navCtrl) }
composable(route = "WorkProfile") { WorkProfile(navCtrl) }
composable(route = "OrgOwnedWorkProfile") { OrgOwnedProfile(navCtrl) }
composable(route = "CreateWorkProfile") { CreateWorkProfile(navCtrl) }
composable(route = "SuspendPersonalApp") { SuspendPersonalApp(navCtrl) }
composable(route = "IntentFilter") { IntentFilter(navCtrl) }
composable(route = "DeleteWorkProfile") { DeleteWorkProfile(navCtrl) }
composable(route = "Applications") { ApplicationManage(navCtrl, dialogStatus) }
composable(route = "UserRestriction") { UserRestriction(navCtrl) }
composable(route = "UR-Internet") {
MyScaffold(R.string.network_and_internet, 0.dp, navCtrl) { RestrictionData.internet.forEach { UserRestrictionItem(it) } }
}
composable(route = "UR-Connectivity") {
MyScaffold(R.string.connectivity, 0.dp, navCtrl) { RestrictionData.connectivity.forEach { UserRestrictionItem(it) } }
}
composable(route = "UR-Applications") {
MyScaffold(R.string.applications, 0.dp, navCtrl) { RestrictionData.applications.forEach { UserRestrictionItem(it) } }
}
composable(route = "UR-Users") {
MyScaffold(R.string.users, 0.dp, navCtrl) { RestrictionData.users.forEach { UserRestrictionItem(it) } }
}
composable(route = "UR-Media") {
MyScaffold(R.string.media, 0.dp, navCtrl) { RestrictionData.media.forEach { UserRestrictionItem(it) } }
}
composable(route = "UR-Other") {
MyScaffold(R.string.other, 0.dp, navCtrl) { RestrictionData.other.forEach { UserRestrictionItem(it) } }
}
composable(route = "Users") { Users(navCtrl) }
composable(route = "UserInfo") { CurrentUserInfo(navCtrl) }
composable(route = "UserOptions") { UserOptions(navCtrl) }
composable(route = "UserOperation") { UserOperation(navCtrl) }
composable(route = "CreateUser") { CreateUser(navCtrl) }
composable(route = "ChangeUsername") { ChangeUsername(navCtrl) }
composable(route = "ChangeUserIcon") { ChangeUserIcon(navCtrl) }
composable(route = "UserSessionMessage") { UserSessionMessage(navCtrl) }
composable(route = "AffiliationID") { AffiliationID(navCtrl) }
composable(route = "Password") { Password(navCtrl) }
composable(route = "PasswordInfo") { PasswordInfo(navCtrl) }
composable(route = "ResetPasswordToken") { ResetPasswordToken(navCtrl) }
composable(route = "ResetPassword") { ResetPassword(navCtrl) }
composable(route = "RequirePasswordComplexity") { PasswordComplexity(navCtrl) }
composable(route = "DisableKeyguardFeatures") { DisableKeyguardFeatures(navCtrl) }
composable(route = "RequirePasswordQuality") { PasswordQuality(navCtrl) }
composable(route = "Settings") { Settings(navCtrl) }
composable(route = "Options") { SettingsOptions(navCtrl) }
composable(route = "Appearance") { Appearance(navCtrl, vm) }
composable(route = "AuthSettings") { AuthSettings(navCtrl) }
composable(route = "Automation") { Automation(navCtrl) }
composable(route = "About") { About(navCtrl) }
composable(route = "PackageSelector") { PackageSelector(navCtrl) }
}
LaunchedEffect(Unit) {
val profileInited = sharedPref.getBoolean("ManagedProfileActivated", false)
val profileNotActivated = !profileInited && context.isProfileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)))
val profileInitialized = sharedPref.getBoolean("ManagedProfileActivated", false)
val profileNotActivated = !profileInitialized && context.isProfileOwner && (VERSION.SDK_INT < 24 || dpm.isManagedProfile(receiver))
if(profileNotActivated) {
dpm.setProfileEnabled(receiver)
sharedPref.edit().putBoolean("ManagedProfileActivated", true).apply()
@@ -203,58 +344,60 @@ private fun HomePage(navCtrl:NavHostController) {
else if(deviceAdmin) R.string.device_admin else R.string.click_to_activate
)
}
Column(modifier = Modifier.background(colorScheme.background).statusBarsPadding().verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 25.dp))
Text(
text = stringResource(R.string.app_name), style = typography.headlineLarge,
modifier = Modifier.padding(start = 10.dp), color = colorScheme.onBackground
)
Spacer(Modifier.padding(vertical = 8.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp, horizontal = 8.dp)
.clip(RoundedCornerShape(15))
.background(color = colorScheme.primary)
.clickable(onClick = { navCtrl.navigate("Permissions") })
.padding(vertical = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Spacer(modifier = Modifier.padding(start = 22.dp))
Icon(
painter = painterResource(if(activated) R.drawable.check_circle_fill1 else R.drawable.block_fill0),
contentDescription = null,
tint = colorScheme.onPrimary
Scaffold {
Column(modifier = Modifier.padding(it).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 25.dp))
Text(
text = stringResource(R.string.app_name), style = typography.headlineLarge,
modifier = Modifier.padding(start = 10.dp)
)
Spacer(modifier = Modifier.padding(start = 10.dp))
Column {
Text(
text = stringResource(if(activated) R.string.activated else R.string.deactivated),
style = typography.headlineSmall,
color = colorScheme.onPrimary,
modifier = Modifier.padding(bottom = 2.dp)
Spacer(Modifier.padding(vertical = 8.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp, horizontal = 8.dp)
.clip(RoundedCornerShape(15))
.background(color = colorScheme.primary)
.clickable(onClick = { navCtrl.navigate("Permissions") })
.padding(vertical = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Spacer(modifier = Modifier.padding(start = 22.dp))
Icon(
painter = painterResource(if(activated) R.drawable.check_circle_fill1 else R.drawable.block_fill0),
contentDescription = null,
tint = colorScheme.onPrimary
)
if(activateType != "") { Text(text = activateType, color = colorScheme.onPrimary) }
Spacer(modifier = Modifier.padding(start = 10.dp))
Column {
Text(
text = stringResource(if(activated) R.string.activated else R.string.deactivated),
style = typography.headlineSmall,
color = colorScheme.onPrimary,
modifier = Modifier.padding(bottom = 2.dp)
)
if(activateType != "") { Text(text = activateType, color = colorScheme.onPrimary) }
}
}
HomePageItem(R.string.system, R.drawable.android_fill0, "System", navCtrl)
if(deviceOwner || profileOwner) { HomePageItem(R.string.network, R.drawable.wifi_fill0, "Network", navCtrl) }
if(
(VERSION.SDK_INT < 24 && !deviceOwner) || (
VERSION.SDK_INT >= 24 && (dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE) ||
(profileOwner && dpm.isManagedProfile(receiver)))
)
) {
HomePageItem(R.string.work_profile, R.drawable.work_fill0, "ManagedProfile", navCtrl)
}
if(deviceOwner || profileOwner) HomePageItem(R.string.applications, R.drawable.apps_fill0, "Applications", navCtrl)
if(VERSION.SDK_INT >= 24 && (profileOwner || deviceOwner)) {
HomePageItem(R.string.user_restriction, R.drawable.person_off, "UserRestriction", navCtrl)
}
HomePageItem(R.string.users,R.drawable.manage_accounts_fill0,"Users", navCtrl)
HomePageItem(R.string.password_and_keyguard, R.drawable.password_fill0, "Password", navCtrl)
HomePageItem(R.string.settings, R.drawable.settings_fill0, "Settings", navCtrl)
Spacer(Modifier.padding(vertical = 20.dp))
}
HomePageItem(R.string.system, R.drawable.android_fill0, "System", navCtrl)
if(deviceOwner || profileOwner) { HomePageItem(R.string.network, R.drawable.wifi_fill0, "Network", navCtrl) }
if(
(VERSION.SDK_INT < 24 && !deviceOwner) || (
VERSION.SDK_INT >= 24 && (dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE) ||
(profileOwner && dpm.isManagedProfile(receiver)))
)
) {
HomePageItem(R.string.work_profile, R.drawable.work_fill0, "ManagedProfile", navCtrl)
}
if(deviceOwner || profileOwner) HomePageItem(R.string.applications, R.drawable.apps_fill0, "Applications", navCtrl)
if(VERSION.SDK_INT >= 24 && (profileOwner || deviceOwner)) {
HomePageItem(R.string.user_restrict, R.drawable.person_off, "UserRestriction", navCtrl)
}
HomePageItem(R.string.users,R.drawable.manage_accounts_fill0,"Users", navCtrl)
HomePageItem(R.string.password_and_keyguard, R.drawable.password_fill0, "Password", navCtrl)
HomePageItem(R.string.settings, R.drawable.settings_fill0, "Settings", navCtrl)
Spacer(Modifier.padding(vertical = 20.dp))
}
}
@@ -263,22 +406,19 @@ fun HomePageItem(name: Int, imgVector: Int, navTo: String, navCtrl: NavHostContr
Row(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(25))
.clickable(onClick = { navCtrl.navigate(navTo) })
.padding(vertical = 13.dp),
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Spacer(Modifier.padding(start = 30.dp))
Icon(
painter = painterResource(imgVector),
contentDescription = null,
tint = colorScheme.onBackground
contentDescription = null
)
Spacer(Modifier.padding(start = 15.dp))
Text(
text = stringResource(name),
style = typography.headlineSmall,
color = colorScheme.onBackground,
modifier = Modifier.padding(bottom = if(zhCN) { 2 } else { 0 }.dp)
)
}

View File

@@ -8,21 +8,15 @@ import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -37,68 +31,36 @@ import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.SubPageItem
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.TopBar
import java.security.SecureRandom
@Composable
fun AppSetting(navCtrl:NavHostController, vm: MyViewModel) {
val localNavCtrl = rememberNavController()
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
Scaffold(
topBar = {
TopBar(backStackEntry, navCtrl, localNavCtrl)
}
) {
NavHost(
navController = localNavCtrl, startDestination = "Home",
enterTransition = Animations.navHostEnterTransition,
exitTransition = Animations.navHostExitTransition,
popEnterTransition = Animations.navHostPopEnterTransition,
popExitTransition = Animations.navHostPopExitTransition,
modifier = Modifier.padding(top = it.calculateTopPadding())
) {
composable(route = "Home") { Home(localNavCtrl) }
composable(route = "Options") { Options() }
composable(route = "Theme") { ThemeSettings(vm) }
composable(route = "Auth") { AuthSettings() }
composable(route = "Automation") { Automation() }
composable(route = "About") { About() }
}
fun Settings(navCtrl: NavHostController) {
MyScaffold(R.string.settings, 0.dp, navCtrl) {
FunctionItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Options") }
FunctionItem(R.string.appearance, "", R.drawable.format_paint_fill0) { navCtrl.navigate("Appearance") }
FunctionItem(R.string.security, "", R.drawable.lock_fill0) { navCtrl.navigate("AuthSettings") }
FunctionItem(R.string.automation_api, "", R.drawable.apps_fill0) { navCtrl.navigate("Automation") }
FunctionItem(R.string.about, "", R.drawable.info_fill0) { navCtrl.navigate("About") }
}
}
@Composable
private fun Home(navCtrl: NavHostController) {
Column(modifier = Modifier.fillMaxSize()) {
SubPageItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Options") }
SubPageItem(R.string.appearance, "", R.drawable.format_paint_fill0) { navCtrl.navigate("Theme") }
SubPageItem(R.string.security, "", R.drawable.lock_fill0) { navCtrl.navigate("Auth") }
SubPageItem(R.string.automation_api, "", R.drawable.apps_fill0) { navCtrl.navigate("Automation") }
SubPageItem(R.string.about, "", R.drawable.info_fill0) { navCtrl.navigate("About") }
}
}
@Composable
private fun Options() {
fun SettingsOptions(navCtrl: NavHostController) {
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(start = 20.dp, end = 16.dp)) {
MyScaffold(R.string.options, 0.dp, navCtrl) {
SwitchItem(
R.string.show_dangerous_features, "", R.drawable.warning_fill0,
{ sharedPref.getBoolean("dangerous_features", false) },
{ sharedPref.edit().putBoolean("dangerous_features", it).apply() }, padding = false
{ sharedPref.edit().putBoolean("dangerous_features", it).apply() }
)
}
}
@Composable
private fun ThemeSettings(vm: MyViewModel) {
fun Appearance(navCtrl: NavHostController, vm: MyViewModel) {
val theme by vm.theme.collectAsStateWithLifecycle()
var darkThemeMenu by remember { mutableStateOf(false) }
val darkThemeTextID = when(theme.darkTheme) {
@@ -106,7 +68,7 @@ private fun ThemeSettings(vm: MyViewModel) {
false -> R.string.off
null -> R.string.follow_system
}
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
MyScaffold(R.string.appearance, 0.dp, navCtrl) {
if(VERSION.SDK_INT >= 31) {
SwitchItem(
R.string.material_you_color, "", null,
@@ -115,7 +77,7 @@ private fun ThemeSettings(vm: MyViewModel) {
)
}
Box {
SubPageItem(R.string.dark_theme, stringResource(darkThemeTextID)) { darkThemeMenu = true }
FunctionItem(R.string.dark_theme, stringResource(darkThemeTextID)) { darkThemeMenu = true }
DropdownMenu(
expanded = darkThemeMenu, onDismissRequest = { darkThemeMenu = false },
offset = DpOffset(x = 25.dp, y = 0.dp)
@@ -154,44 +116,42 @@ private fun ThemeSettings(vm: MyViewModel) {
}
@Composable
private fun AuthSettings() {
fun AuthSettings(navCtrl: NavHostController) {
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
var auth by remember{ mutableStateOf(sharedPref.getBoolean("auth",false)) }
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(start = 20.dp, end = 16.dp)) {
MyScaffold(R.string.security, 0.dp, navCtrl) {
SwitchItem(
R.string.lock_owndroid, "", null, auth,
{
sharedPref.edit().putBoolean("auth", it).apply()
auth = sharedPref.getBoolean("auth", false)
}, padding = false
}
)
if(auth) {
SwitchItem(
R.string.enable_bio_auth, "", null,
{ sharedPref.getBoolean("bio_auth", false) },
{ sharedPref.edit().putBoolean("bio_auth", it).apply() }, padding = false
{ sharedPref.edit().putBoolean("bio_auth", it).apply() }
)
SwitchItem(
R.string.lock_in_background, stringResource(R.string.developing), null,
{ sharedPref.getBoolean("lock_in_background", false) },
{ sharedPref.edit().putBoolean("lock_in_background", it).apply() }, padding = 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() }, padding = false
{ sharedPref.edit().putBoolean("protect_storage", it).apply() }
)
}
}
@Composable
private fun Automation() {
fun Automation(navCtrl: NavHostController) {
val context = LocalContext.current
val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE)
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.automation_api), style = typography.headlineLarge)
MyScaffold(R.string.automation_api, 8.dp, navCtrl) {
Spacer(Modifier.padding(vertical = 5.dp))
var key by remember { mutableStateOf("") }
OutlinedTextField(
@@ -229,18 +189,15 @@ private fun Automation() {
}
@Composable
private fun About() {
fun About(navCtrl: NavHostController) {
val context = LocalContext.current
val pkgInfo = context.packageManager.getPackageInfo(context.packageName,0)
val verCode = pkgInfo.versionCode
val verName = pkgInfo.versionName
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.about), style = typography.headlineLarge, modifier = Modifier.padding(start = 26.dp))
MyScaffold(R.string.about, 0.dp, navCtrl) {
Text(text = stringResource(R.string.app_name)+" v$verName ($verCode)", modifier = Modifier.padding(start = 16.dp))
Spacer(Modifier.padding(vertical = 5.dp))
Text(text = stringResource(R.string.app_name)+" v$verName ($verCode)", modifier = Modifier.padding(start = 26.dp))
Spacer(Modifier.padding(vertical = 5.dp))
SubPageItem(R.string.project_homepage, "GitHub", R.drawable.open_in_new) { shareLink(context, "https://github.com/BinTianqi/OwnDroid") }
FunctionItem(R.string.project_homepage, "GitHub", R.drawable.open_in_new) { shareLink(context, "https://github.com/BinTianqi/OwnDroid") }
}
}

View File

@@ -90,7 +90,7 @@ import com.bintianqi.owndroid.ui.Information
import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.ui.SubPageItem
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.SwitchItem
import java.util.concurrent.Executors
@@ -119,7 +119,7 @@ fun ApplicationManage(navCtrl:NavHostController, dialogStatus: MutableIntState)
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
trailingIcon = {
Icon(painter = painterResource(R.drawable.checklist_fill0), contentDescription = null,
Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null,
modifier = Modifier
.clip(RoundedCornerShape(50))
.clickable(onClick = {
@@ -216,7 +216,7 @@ private fun Home(
if(VERSION.SDK_INT >= 24 && profileOwner && dpm.isManagedProfile(receiver)) {
Text(text = stringResource(R.string.scope_is_work_profile), textAlign = TextAlign.Center,modifier = Modifier.fillMaxWidth())
}
SubPageItem(R.string.app_info,"", R.drawable.open_in_new) {
FunctionItem(R.string.app_info,"", R.drawable.open_in_new) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.setData(Uri.parse("package:$pkgName"))
startActivity(context, intent, null)
@@ -242,37 +242,37 @@ private fun Home(
onClickBlank = { appControlAction = 3; appControlDialog = true }
)
if((VERSION.SDK_INT >= 33 && profileOwner) || (VERSION.SDK_INT >= 30 && deviceOwner)) {
SubPageItem(R.string.ucd, "", R.drawable.do_not_touch_fill0) { navCtrl.navigate("UserControlDisabled") }
FunctionItem(R.string.ucd, "", R.drawable.do_not_touch_fill0) { navCtrl.navigate("UserControlDisabled") }
}
if(VERSION.SDK_INT>=23) {
SubPageItem(R.string.permission_manage, "", R.drawable.key_fill0) { navCtrl.navigate("PermissionManage") }
FunctionItem(R.string.permission_manage, "", R.drawable.key_fill0) { navCtrl.navigate("PermissionManage") }
}
if(VERSION.SDK_INT >= 30 && profileOwner && dpm.isManagedProfile(receiver)) {
SubPageItem(R.string.cross_profile_package, "", R.drawable.work_fill0) { navCtrl.navigate("CrossProfilePackage") }
FunctionItem(R.string.cross_profile_package, "", R.drawable.work_fill0) { navCtrl.navigate("CrossProfilePackage") }
}
if(profileOwner) {
SubPageItem(R.string.cross_profile_widget, "", R.drawable.widgets_fill0) { navCtrl.navigate("CrossProfileWidget") }
FunctionItem(R.string.cross_profile_widget, "", R.drawable.widgets_fill0) { navCtrl.navigate("CrossProfileWidget") }
}
if(VERSION.SDK_INT >= 34 && deviceOwner) {
SubPageItem(R.string.credential_manage_policy, "", R.drawable.license_fill0) { navCtrl.navigate("CredentialManagePolicy") }
FunctionItem(R.string.credential_manage_policy, "", R.drawable.license_fill0) { navCtrl.navigate("CredentialManagePolicy") }
}
SubPageItem(R.string.permitted_accessibility_services, "", R.drawable.settings_accessibility_fill0) { navCtrl.navigate("Accessibility") }
SubPageItem(R.string.permitted_ime, "", R.drawable.keyboard_fill0) { navCtrl.navigate("IME") }
SubPageItem(R.string.enable_system_app, "", R.drawable.enable_fill0) {
FunctionItem(R.string.permitted_accessibility_services, "", R.drawable.settings_accessibility_fill0) { navCtrl.navigate("Accessibility") }
FunctionItem(R.string.permitted_ime, "", R.drawable.keyboard_fill0) { navCtrl.navigate("IME") }
FunctionItem(R.string.enable_system_app, "", R.drawable.enable_fill0) {
if(pkgName != "") dialogStatus.intValue = 1
}
if(VERSION.SDK_INT >= 28 && deviceOwner) {
SubPageItem(R.string.keep_uninstalled_packages, "", R.drawable.delete_fill0) { navCtrl.navigate("KeepUninstalled") }
FunctionItem(R.string.keep_uninstalled_packages, "", R.drawable.delete_fill0) { navCtrl.navigate("KeepUninstalled") }
}
if(VERSION.SDK_INT >= 28) {
SubPageItem(R.string.clear_app_storage, "", R.drawable.mop_fill0) {
FunctionItem(R.string.clear_app_storage, "", R.drawable.mop_fill0) {
if(pkgName != "") dialogStatus.intValue = 2
}
}
SubPageItem(R.string.install_app, "", R.drawable.install_mobile_fill0) { navCtrl.navigate("InstallApp") }
SubPageItem(R.string.uninstall_app, "", R.drawable.delete_fill0) { navCtrl.navigate("UninstallApp") }
FunctionItem(R.string.install_app, "", R.drawable.install_mobile_fill0) { navCtrl.navigate("InstallApp") }
FunctionItem(R.string.uninstall_app, "", R.drawable.delete_fill0) { navCtrl.navigate("UninstallApp") }
if(VERSION.SDK_INT >= 34 && (deviceOwner || dpm.isOrgProfile(receiver))) {
SubPageItem(R.string.set_default_dialer, "", R.drawable.call_fill0) {
FunctionItem(R.string.set_default_dialer, "", R.drawable.call_fill0) {
if(pkgName != "") dialogStatus.intValue = 3
}
}

View File

@@ -153,7 +153,7 @@ private fun binderWrapperPackageInstaller(appContext: Context): PackageInstaller
fun Context.getPI(): PackageInstaller {
val sharedPref = this.getSharedPreferences("data", Context.MODE_PRIVATE)
if(sharedPref.getBoolean("dhizuku", false)) {
if (!Dhizuku.isPermissionGranted()) {
if (!dhizukuPermissionGranted()) {
dhizukuErrorStatus.value = 2
backToHomeStateFlow.value = true
return this.packageManager.packageInstaller
@@ -167,7 +167,7 @@ fun Context.getPI(): PackageInstaller {
fun Context.getDPM(): DevicePolicyManager {
val sharedPref = this.getSharedPreferences("data", Context.MODE_PRIVATE)
if(sharedPref.getBoolean("dhizuku", false)) {
if (!Dhizuku.isPermissionGranted()) {
if (!dhizukuPermissionGranted()) {
dhizukuErrorStatus.value = 2
backToHomeStateFlow.value = true
return this.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
@@ -193,7 +193,7 @@ fun Context.resetDevicePolicy() {
val dpm = getDPM()
val receiver = getReceiver()
RestrictionData.getAllRestrictions().forEach {
dpm.clearUserRestriction(receiver, it)
dpm.clearUserRestriction(receiver, it.id)
}
dpm.accountTypesWithManagementDisabled?.forEach {
dpm.setAccountManagementDisabled(receiver, it, false)
@@ -428,7 +428,7 @@ fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? {
val payload = event.data as Array<*>
buildJsonObject {
put("mac", payload[0] as String)
(payload[2] as String).let { if(it != "") put("reason", it) }
(payload[1] as String).let { if(it != "") put("reason", it) }
}
}
SecurityLog.TAG_CAMERA_POLICY_SET -> {
@@ -619,3 +619,11 @@ fun setDefaultAffiliationID(context: Context) {
}
}
}
fun dhizukuPermissionGranted() =
try {
Dhizuku.isPermissionGranted()
} catch(_: Exception) {
false
}

View File

@@ -22,21 +22,17 @@ import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@@ -55,88 +51,47 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.CardItem
import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.CopyTextButton
import com.bintianqi.owndroid.ui.InfoCard
import com.bintianqi.owndroid.ui.SubPageItem
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.TopBar
import com.bintianqi.owndroid.yesOrNo
@Composable
fun ManagedProfile(navCtrl: NavHostController) {
val localNavCtrl = rememberNavController()
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
Scaffold(
topBar = {
TopBar(backStackEntry, navCtrl, localNavCtrl)
}
) {
NavHost(
navController = localNavCtrl, startDestination = "Home",
enterTransition = Animations.navHostEnterTransition,
exitTransition = Animations.navHostExitTransition,
popEnterTransition = Animations.navHostPopEnterTransition,
popExitTransition = Animations.navHostPopExitTransition,
modifier = Modifier.padding(top = it.calculateTopPadding())
) {
composable(route = "Home") { Home(localNavCtrl) }
composable(route = "OrgOwnedWorkProfile") { OrgOwnedProfile() }
composable(route = "CreateWorkProfile") { CreateWorkProfile() }
composable(route = "SuspendPersonalApp") { SuspendPersonalApp() }
composable(route = "IntentFilter") { IntentFilter() }
composable(route = "DeleteWorkProfile") { DeleteWorkProfile() }
}
}
}
@Composable
private fun Home(navCtrl: NavHostController) {
fun WorkProfile(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val profileOwner = context.isProfileOwner
Column(
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())
) {
Text(
text = stringResource(R.string.work_profile),
style = typography.headlineLarge,
modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 16.dp)
)
MyScaffold(R.string.work_profile, 0.dp, navCtrl) {
if(VERSION.SDK_INT >= 30 && profileOwner && dpm.isManagedProfile(receiver)) {
SubPageItem(R.string.org_owned_work_profile, "", R.drawable.corporate_fare_fill0) { navCtrl.navigate("OrgOwnedWorkProfile") }
FunctionItem(R.string.org_owned_work_profile, "", R.drawable.corporate_fare_fill0) { navCtrl.navigate("OrgOwnedWorkProfile") }
}
if(VERSION.SDK_INT<24 || (VERSION.SDK_INT>=24 && dpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE))) {
SubPageItem(R.string.create_work_profile, "", R.drawable.work_fill0) { navCtrl.navigate("CreateWorkProfile") }
FunctionItem(R.string.create_work_profile, "", R.drawable.work_fill0) { navCtrl.navigate("CreateWorkProfile") }
}
if(dpm.isOrgProfile(receiver)) {
SubPageItem(R.string.suspend_personal_app, "", R.drawable.block_fill0) { navCtrl.navigate("SuspendPersonalApp") }
FunctionItem(R.string.suspend_personal_app, "", R.drawable.block_fill0) { navCtrl.navigate("SuspendPersonalApp") }
}
if(profileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)))) {
SubPageItem(R.string.intent_filter, "", R.drawable.filter_alt_fill0) { navCtrl.navigate("IntentFilter") }
FunctionItem(R.string.intent_filter, "", R.drawable.filter_alt_fill0) { navCtrl.navigate("IntentFilter") }
}
if(profileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)))) {
SubPageItem(R.string.delete_work_profile, "", R.drawable.delete_forever_fill0) { navCtrl.navigate("DeleteWorkProfile") }
FunctionItem(R.string.delete_work_profile, "", R.drawable.delete_forever_fill0) { navCtrl.navigate("DeleteWorkProfile") }
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@Composable
private fun CreateWorkProfile() {
fun CreateWorkProfile(navCtrl: NavHostController) {
val context = LocalContext.current
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.create_work_profile), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.create_work_profile, 8.dp, navCtrl) {
var skipEncrypt by remember { mutableStateOf(false) }
var offlineProvisioning by remember { mutableStateOf(true) }
var migrateAccount by remember { mutableStateOf(false) }
@@ -206,14 +161,11 @@ private fun CreateWorkProfile() {
@SuppressLint("NewApi")
@Composable
private fun OrgOwnedProfile() {
fun OrgOwnedProfile(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.org_owned_work_profile), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
Text(text = stringResource(R.string.is_org_owned_profile,dpm.isOrganizationOwnedDeviceWithManagedProfile))
MyScaffold(R.string.org_owned_work_profile, 8.dp, navCtrl, false) {
CardItem(R.string.org_owned_work_profile, dpm.isOrganizationOwnedDeviceWithManagedProfile.yesOrNo)
Spacer(Modifier.padding(vertical = 5.dp))
if(!dpm.isOrganizationOwnedDeviceWithManagedProfile) {
SelectionContainer {
@@ -229,14 +181,13 @@ private fun OrgOwnedProfile() {
@SuppressLint("NewApi")
@Composable
private fun SuspendPersonalApp() {
fun SuspendPersonalApp(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
var suspend by remember { mutableStateOf(dpm.getPersonalAppsSuspendedReasons(receiver) != PERSONAL_APPS_NOT_SUSPENDED) }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
MyScaffold(R.string.suspend_personal_app, 8.dp, navCtrl) {
SwitchItem(
R.string.suspend_personal_app, "", null,
suspend,
@@ -277,16 +228,13 @@ private fun SuspendPersonalApp() {
}
@Composable
private fun IntentFilter() {
fun IntentFilter(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
MyScaffold(R.string.intent_filter, 8.dp, navCtrl) {
var action by remember { mutableStateOf("") }
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.intent_filter), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
OutlinedTextField(
value = action, onValueChange = { action = it },
label = { Text("Action") },
@@ -328,7 +276,7 @@ private fun IntentFilter() {
}
@Composable
private fun DeleteWorkProfile() {
fun DeleteWorkProfile(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val focusMgr = LocalFocusManager.current
@@ -337,14 +285,7 @@ private fun DeleteWorkProfile() {
var euicc by remember { mutableStateOf(false) }
var silent by remember { mutableStateOf(false) }
var reason by remember { mutableStateOf("") }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(
text = stringResource(R.string.delete_work_profile),
style = typography.headlineLarge,
modifier = Modifier.padding(6.dp),color = colorScheme.error
)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.delete_work_profile, 8.dp, navCtrl) {
CheckBoxItem(R.string.wipe_external_storage, externalStorage, { externalStorage = it })
if(VERSION.SDK_INT >= 28) { CheckBoxItem(R.string.wipe_euicc, euicc, { euicc = it }) }
CheckBoxItem(R.string.wipe_silently, silent, { silent = it })
@@ -367,7 +308,6 @@ private fun DeleteWorkProfile() {
) {
Text(stringResource(R.string.delete))
}
Spacer(Modifier.padding(vertical = 30.dp))
}
if(warning) {
LaunchedEffect(Unit) { silent = reason == "" }

View File

@@ -43,21 +43,17 @@ import android.telephony.data.ApnSetting.PROTOCOL_UNSTRUCTURED
import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
@@ -69,14 +65,12 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
@@ -88,7 +82,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
@@ -99,72 +92,73 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.exportFile
import com.bintianqi.owndroid.exportFilePath
import com.bintianqi.owndroid.formatFileSize
import com.bintianqi.owndroid.selectedPackage
import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard
import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.ui.SubPageItem
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.TopBar
import com.bintianqi.owndroid.writeClipBoard
import kotlin.math.max
@Composable
fun Network(navCtrl: NavHostController) {
val localNavCtrl = rememberNavController()
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
val scrollState = rememberScrollState()
val wifiMacDialog = remember { mutableStateOf(false) }
Scaffold(
topBar = {
TopBar(backStackEntry,navCtrl,localNavCtrl) {
if(backStackEntry?.destination?.route == "Home" && scrollState.maxValue > 80) {
Text(
text = stringResource(R.string.network),
modifier = Modifier.alpha((maxOf(scrollState.value-30,0)).toFloat()/80)
)
}
}
fun Network(navCtrl:NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE)
val dhizuku = sharedPref.getBoolean("dhizuku", false)
var wifiMacDialog by remember { mutableStateOf(false) }
MyScaffold(R.string.network, 0.dp, navCtrl) {
if(VERSION.SDK_INT >= 24 && (deviceOwner || dpm.isOrgProfile(receiver))) {
FunctionItem(R.string.wifi_mac_address, "", R.drawable.wifi_fill0) { wifiMacDialog = true }
}
) {
NavHost(
navController = localNavCtrl, startDestination = "Home",
enterTransition = Animations.navHostEnterTransition,
exitTransition = Animations.navHostExitTransition,
popEnterTransition = Animations.navHostPopEnterTransition,
popExitTransition = Animations.navHostPopExitTransition,
modifier = Modifier.padding(top = it.calculateTopPadding())
) {
composable(route = "Home") { Home(localNavCtrl, scrollState, wifiMacDialog) }
composable(route = "Switches") { Switches() }
composable(route = "MinWifiSecurityLevel") { WifiSecLevel() }
composable(route = "WifiSsidPolicy") { WifiSsidPolicy() }
composable(route = "PrivateDNS") { PrivateDNS() }
composable(route = "AlwaysOnVpn") { AlwaysOnVPNPackage(navCtrl) }
composable(route = "RecommendedGlobalProxy") { RecommendedGlobalProxy() }
composable(route = "NetworkLog") { NetworkLog() }
composable(route = "WifiAuthKeypair") { WifiAuthKeypair() }
composable(route = "PreferentialNetworkService") { PreferentialNetworkService() }
composable(route = "APN") { APN() }
if(VERSION.SDK_INT >= 30) {
FunctionItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("NetworkOptions") }
}
if(VERSION.SDK_INT >= 33 && (deviceOwner || dpm.isOrgProfile(receiver))) {
FunctionItem(R.string.min_wifi_security_level, "", R.drawable.wifi_password_fill0) { navCtrl.navigate("MinWifiSecurityLevel") }
}
if(VERSION.SDK_INT >= 33 && (deviceOwner || dpm.isOrgProfile(receiver))) {
FunctionItem(R.string.wifi_ssid_policy, "", R.drawable.wifi_fill0) { navCtrl.navigate("WifiSsidPolicy") }
}
if(VERSION.SDK_INT >= 29 && deviceOwner) {
FunctionItem(R.string.private_dns, "", R.drawable.dns_fill0) { navCtrl.navigate("PrivateDNS") }
}
if(VERSION.SDK_INT >= 24 && (deviceOwner || profileOwner)) {
FunctionItem(R.string.always_on_vpn, "", R.drawable.vpn_key_fill0) { navCtrl.navigate("AlwaysOnVpn") }
}
if(deviceOwner) {
FunctionItem(R.string.recommended_global_proxy, "", R.drawable.vpn_key_fill0) { navCtrl.navigate("RecommendedGlobalProxy") }
}
if(VERSION.SDK_INT >= 26 && !dhizuku && (deviceOwner || (profileOwner && dpm.isManagedProfile(receiver)))) {
FunctionItem(R.string.network_logging, "", R.drawable.description_fill0) { navCtrl.navigate("NetworkLog") }
}
if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) {
FunctionItem(R.string.wifi_auth_keypair, "", R.drawable.key_fill0) { navCtrl.navigate("WifiAuthKeypair") }
}
if(VERSION.SDK_INT >= 33 && (deviceOwner || profileOwner)) {
FunctionItem(R.string.preferential_network_service, "", R.drawable.globe_fill0) { navCtrl.navigate("PreferentialNetworkService") }
}
if(VERSION.SDK_INT >= 28 && deviceOwner) {
FunctionItem(R.string.override_apn_settings, "", R.drawable.cell_tower_fill0) { navCtrl.navigate("OverrideAPN") }
}
}
if(wifiMacDialog.value && VERSION.SDK_INT >= 24) {
if(wifiMacDialog && VERSION.SDK_INT >= 24) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
AlertDialog(
onDismissRequest = { wifiMacDialog.value = false },
confirmButton = { TextButton(onClick = { wifiMacDialog.value = false }) { Text(stringResource(R.string.confirm)) } },
onDismissRequest = { wifiMacDialog = false },
confirmButton = { TextButton(onClick = { wifiMacDialog = false }) { Text(stringResource(R.string.confirm)) } },
title = { Text(stringResource(R.string.wifi_mac_address)) },
text = {
val mac = dpm.getWifiMacAddress(receiver)
@@ -184,66 +178,13 @@ fun Network(navCtrl: NavHostController) {
}
@Composable
private fun Home(navCtrl:NavHostController, scrollState: ScrollState, wifiMacDialog: MutableState<Boolean>) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE)
val dhizuku = sharedPref.getBoolean("dhizuku", false)
Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) {
Text(
text = stringResource(R.string.network),
style = typography.headlineLarge,
modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 16.dp)
)
if(VERSION.SDK_INT >= 24 && (deviceOwner || dpm.isOrgProfile(receiver))) {
SubPageItem(R.string.wifi_mac_address, "", R.drawable.wifi_fill0) { wifiMacDialog.value = true }
}
if(VERSION.SDK_INT >= 30) {
SubPageItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Switches") }
}
if(VERSION.SDK_INT >= 33 && (deviceOwner || dpm.isOrgProfile(receiver))) {
SubPageItem(R.string.min_wifi_security_level, "", R.drawable.wifi_password_fill0) { navCtrl.navigate("MinWifiSecurityLevel") }
}
if(VERSION.SDK_INT >= 33 && (deviceOwner || dpm.isOrgProfile(receiver))) {
SubPageItem(R.string.wifi_ssid_policy, "", R.drawable.wifi_fill0) { navCtrl.navigate("WifiSsidPolicy") }
}
if(VERSION.SDK_INT >= 29 && deviceOwner) {
SubPageItem(R.string.private_dns, "", R.drawable.dns_fill0) { navCtrl.navigate("PrivateDNS") }
}
if(VERSION.SDK_INT >= 24 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.always_on_vpn, "", R.drawable.vpn_key_fill0) { navCtrl.navigate("AlwaysOnVpn") }
}
if(deviceOwner) {
SubPageItem(R.string.recommended_global_proxy, "", R.drawable.vpn_key_fill0) { navCtrl.navigate("RecommendedGlobalProxy") }
}
if(VERSION.SDK_INT >= 26 && !dhizuku && (deviceOwner || (profileOwner && dpm.isManagedProfile(receiver)))) {
SubPageItem(R.string.retrieve_net_logs, "", R.drawable.description_fill0) { navCtrl.navigate("NetworkLog") }
}
if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.wifi_auth_keypair, "", R.drawable.key_fill0) { navCtrl.navigate("WifiAuthKeypair") }
}
if(VERSION.SDK_INT >= 33 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.preferential_network_service, "", R.drawable.globe_fill0) { navCtrl.navigate("PreferentialNetworkService") }
}
if(VERSION.SDK_INT >= 28 && deviceOwner) {
SubPageItem(R.string.override_apn_settings, "", R.drawable.cell_tower_fill0) { navCtrl.navigate("APN") }
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@Composable
private fun Switches() {
fun NetworkOptions(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
var dialog by remember { mutableIntStateOf(0) }
Column(modifier = Modifier.fillMaxSize()) {
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.options, 0.dp, navCtrl) {
if(VERSION.SDK_INT>=30 && (deviceOwner || dpm.isOrgProfile(receiver))) {
SwitchItem(R.string.lockdown_admin_configured_network, "", R.drawable.wifi_password_fill0,
{ dpm.hasLockdownAdminConfiguredNetworks(receiver) }, { dpm.setConfiguredNetworksLockdownState(receiver,it) },
@@ -262,15 +203,12 @@ private fun Switches() {
@SuppressLint("NewApi")
@Composable
private fun WifiSecLevel() {
fun WifiSecurityLevel(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
var selectedWifiSecLevel by remember { mutableIntStateOf(0) }
LaunchedEffect(Unit) { selectedWifiSecLevel = dpm.minimumRequiredWifiSecurityLevel }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.min_wifi_security_level), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.min_wifi_security_level, 8.dp, navCtrl) {
RadioButtonItem(
R.string.wifi_security_open,
selectedWifiSecLevel == WIFI_SECURITY_OPEN,
@@ -307,11 +245,11 @@ private fun WifiSecLevel() {
@SuppressLint("NewApi")
@Composable
private fun WifiSsidPolicy() {
fun WifiSsidPolicy(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val focusMgr = LocalFocusManager.current
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
MyScaffold(R.string.wifi_ssid_policy, 8.dp, navCtrl) {
var selectedPolicyType by remember { mutableIntStateOf(-1) }
val ssidList = remember { mutableStateListOf<WifiSsid>() }
val refreshPolicy = {
@@ -321,9 +259,6 @@ private fun WifiSsidPolicy() {
ssidList.addAll(policy?.ssids ?: mutableSetOf())
}
LaunchedEffect(Unit) { refreshPolicy() }
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.wifi_ssid_policy), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
RadioButtonItem(
R.string.none,
selectedPolicyType == -1,
@@ -387,20 +322,17 @@ private fun WifiSsidPolicy() {
) {
Text(stringResource(R.string.apply))
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@SuppressLint("NewApi")
@Composable
private fun PrivateDNS() {
fun PrivateDNS(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.private_dns), style = typography.headlineLarge)
MyScaffold(R.string.private_dns, 8.dp, navCtrl) {
val dnsStatus = mapOf(
PRIVATE_DNS_MODE_UNKNOWN to stringResource(R.string.unknown),
PRIVATE_DNS_MODE_OFF to stringResource(R.string.disabled),
@@ -462,7 +394,6 @@ private fun PrivateDNS() {
Text(stringResource(R.string.set_dns_host))
}
InfoCard(R.string.info_set_private_dns_host)
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@@ -499,9 +430,7 @@ fun AlwaysOnVPNPackage(navCtrl: NavHostController) {
false
}
}
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.always_on_vpn), style = typography.headlineLarge, modifier = Modifier.padding(vertical = 8.dp))
MyScaffold(R.string.always_on_vpn, 8.dp, navCtrl) {
OutlinedTextField(
value = pkgName,
onValueChange = { pkgName = it },
@@ -509,7 +438,7 @@ fun AlwaysOnVPNPackage(navCtrl: NavHostController) {
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
trailingIcon = {
Icon(painter = painterResource(R.drawable.checklist_fill0), contentDescription = null,
Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null,
modifier = Modifier
.clip(RoundedCornerShape(50))
.clickable(onClick = {
@@ -536,12 +465,11 @@ fun AlwaysOnVPNPackage(navCtrl: NavHostController) {
Text(stringResource(R.string.clear_current_config))
}
InfoCard(R.string.info_always_on_vpn)
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@Composable
private fun RecommendedGlobalProxy() {
fun RecommendedGlobalProxy(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -551,10 +479,7 @@ private fun RecommendedGlobalProxy() {
var specifyPort by remember { mutableStateOf(false) }
var proxyPort by remember { mutableStateOf("") }
var exclList by remember { mutableStateOf("") }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.recommended_global_proxy), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.recommended_global_proxy, 8.dp, navCtrl) {
RadioButtonItem(R.string.proxy_type_off, proxyType == 0, { proxyType = 0 })
RadioButtonItem(R.string.proxy_type_pac, proxyType == 1, { proxyType = 1 })
RadioButtonItem(R.string.proxy_type_direct, proxyType == 2, { proxyType = 2 })
@@ -641,7 +566,7 @@ private fun RecommendedGlobalProxy() {
@SuppressLint("NewApi")
@Composable
private fun NetworkLog() {
fun NetworkLogging(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -650,10 +575,7 @@ private fun NetworkLog() {
LaunchedEffect(Unit) {
fileSize = logFile.length()
}
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.retrieve_net_logs), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.network_logging, 8.dp, navCtrl) {
SwitchItem(R.string.enable, "", null, { dpm.isNetworkLoggingEnabled(receiver) }, { dpm.setNetworkLoggingEnabled(receiver,it) }, padding = false)
Text(stringResource(R.string.log_file_size_is, formatFileSize(fileSize)))
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
@@ -688,15 +610,12 @@ private fun NetworkLog() {
@SuppressLint("NewApi")
@Composable
private fun WifiAuthKeypair() {
fun WifiAuthKeypair(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val focusMgr = LocalFocusManager.current
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
var keyPair by remember { mutableStateOf("") }
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.wifi_auth_keypair), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
var keyPair by remember { mutableStateOf("") }
MyScaffold(R.string.wifi_auth_keypair, 8.dp, navCtrl) {
OutlinedTextField(
value = keyPair,
label = { Text(stringResource(R.string.alias)) },
@@ -739,7 +658,7 @@ private fun WifiAuthKeypair() {
@SuppressLint("NewApi")
@Composable
fun PreferentialNetworkService() {
fun PreferentialNetworkService(navCtrl: NavHostController) {
val focusMgr = LocalFocusManager.current
val context = LocalContext.current
val dpm = context.getDPM()
@@ -778,10 +697,7 @@ fun PreferentialNetworkService() {
refresh()
}
LaunchedEffect(Unit) { initialize() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.preferential_network_service), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.preferential_network_service, 8.dp, navCtrl) {
SwitchItem(
title = R.string.enabled, desc = "", icon = null,
state = masterEnabled, onCheckedChange = { masterEnabled = it }, padding = false
@@ -895,25 +811,21 @@ fun PreferentialNetworkService() {
) {
Text(stringResource(R.string.apply))
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@SuppressLint("NewApi")
@Composable
private fun APN() {
fun OverrideAPN(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
val setting = dpm.getOverrideApns(receiver)
var inputNum by remember { mutableStateOf("0") }
var nextStep by remember { mutableStateOf(false) }
val builder = Builder()
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.override_apn_settings), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
val setting = dpm.getOverrideApns(receiver)
var inputNum by remember { mutableStateOf("0") }
var nextStep by remember { mutableStateOf(false) }
val builder = Builder()
MyScaffold(R.string.override_apn_settings, 8.dp, navCtrl) {
Text(text = stringResource(id = R.string.developing))
Spacer(Modifier.padding(vertical = 5.dp))
SwitchItem(R.string.enable, "", null, { dpm.isOverrideApnEnabled(receiver) }, { dpm.setOverrideApnsEnabled(receiver,it) }, padding = false)
@@ -1275,6 +1187,5 @@ private fun APN() {
}
}
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}

View File

@@ -35,25 +35,20 @@ import android.os.Build.VERSION
import android.os.UserManager
import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@@ -65,7 +60,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
@@ -75,103 +69,56 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat.startActivity
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.toggle
import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.CardItem
import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard
import com.bintianqi.owndroid.ui.Information
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.ui.SubPageItem
import com.bintianqi.owndroid.ui.TopBar
import com.bintianqi.owndroid.yesOrNo
@Composable
fun Password(navCtrl: NavHostController) {
val localNavCtrl = rememberNavController()
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
val scrollState = rememberScrollState()
Scaffold(
topBar = {
TopBar(backStackEntry,navCtrl,localNavCtrl) {
if(backStackEntry?.destination?.route == "Home" && scrollState.maxValue > 100) {
Text(
text = stringResource(R.string.password_and_keyguard),
modifier = Modifier.alpha((maxOf(scrollState.value-30,0)).toFloat()/80)
)
}
}
}
) {
NavHost(
navController = localNavCtrl, startDestination = "Home",
enterTransition = Animations.navHostEnterTransition,
exitTransition = Animations.navHostExitTransition,
popEnterTransition = Animations.navHostPopEnterTransition,
popExitTransition = Animations.navHostPopExitTransition,
modifier = Modifier.padding(top = it.calculateTopPadding())
) {
composable(route = "Home") { Home(localNavCtrl,scrollState) }
composable(route = "PasswordInfo") { PasswordInfo() }
composable(route = "ResetPasswordToken") { ResetPasswordToken() }
composable(route = "ResetPassword") { ResetPassword() }
composable(route = "RequirePasswordComplexity") { PasswordComplexity() }
composable(route = "DisableKeyguardFeatures") { DisableKeyguardFeatures() }
composable(route = "RequirePasswordQuality") { PasswordQuality() }
}
}
}
@SuppressLint("NewApi")
@Composable
private fun Home(navCtrl:NavHostController, scrollState: ScrollState) {
fun Password(navCtrl: NavHostController) {
val context = LocalContext.current
val sharedPrefs = context.getSharedPreferences("data", Context.MODE_PRIVATE)
val deviceAdmin = context.isDeviceAdmin
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
var dialog by remember { mutableIntStateOf(0) }
Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) {
Text(
text = stringResource(R.string.password_and_keyguard),
style = typography.headlineLarge,
modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 16.dp)
)
SubPageItem(R.string.password_info, "", R.drawable.info_fill0) { navCtrl.navigate("PasswordInfo") }
MyScaffold(R.string.password_and_keyguard, 0.dp, navCtrl) {
FunctionItem(R.string.password_info, "", R.drawable.info_fill0) { navCtrl.navigate("PasswordInfo") }
if(sharedPrefs.getBoolean("dangerous_features", false)) {
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.reset_password_token, "", R.drawable.key_vertical_fill0) { navCtrl.navigate("ResetPasswordToken") }
FunctionItem(R.string.reset_password_token, "", R.drawable.key_vertical_fill0) { navCtrl.navigate("ResetPasswordToken") }
}
if(deviceAdmin || deviceOwner || profileOwner) {
SubPageItem(R.string.reset_password, "", R.drawable.lock_reset_fill0) { navCtrl.navigate("ResetPassword") }
FunctionItem(R.string.reset_password, "", R.drawable.lock_reset_fill0) { navCtrl.navigate("ResetPassword") }
}
}
if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.required_password_complexity, "", R.drawable.password_fill0) { navCtrl.navigate("RequirePasswordComplexity") }
FunctionItem(R.string.required_password_complexity, "", R.drawable.password_fill0) { navCtrl.navigate("RequirePasswordComplexity") }
}
if(deviceAdmin) {
SubPageItem(R.string.disable_keyguard_features, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("DisableKeyguardFeatures") }
FunctionItem(R.string.disable_keyguard_features, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("DisableKeyguardFeatures") }
}
if(deviceOwner) {
SubPageItem(R.string.max_time_to_lock, "", R.drawable.schedule_fill0) { dialog = 1 }
SubPageItem(R.string.pwd_expiration_timeout, "", R.drawable.lock_clock_fill0) { dialog = 3 }
SubPageItem(R.string.max_pwd_fail, "", R.drawable.no_encryption_fill0) { dialog = 4 }
FunctionItem(R.string.max_time_to_lock, "", R.drawable.schedule_fill0) { dialog = 1 }
FunctionItem(R.string.pwd_expiration_timeout, "", R.drawable.lock_clock_fill0) { dialog = 3 }
FunctionItem(R.string.max_pwd_fail, "", R.drawable.no_encryption_fill0) { dialog = 4 }
}
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.required_strong_auth_timeout, "", R.drawable.fingerprint_off_fill0) { dialog = 2 }
FunctionItem(R.string.required_strong_auth_timeout, "", R.drawable.fingerprint_off_fill0) { dialog = 2 }
}
if(deviceAdmin){
SubPageItem(R.string.pwd_history, "", R.drawable.history_fill0) { dialog = 5 }
FunctionItem(R.string.pwd_history, "", R.drawable.history_fill0) { dialog = 5 }
}
if(VERSION.SDK_INT < 31 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.required_password_quality, "", R.drawable.password_fill0) { navCtrl.navigate("RequirePasswordQuality") }
FunctionItem(R.string.required_password_quality, "", R.drawable.password_fill0) { navCtrl.navigate("RequirePasswordQuality") }
}
Spacer(Modifier.padding(vertical = 30.dp))
}
if(dialog != 0) {
val dpm = context.getDPM()
@@ -263,16 +210,13 @@ private fun Home(navCtrl:NavHostController, scrollState: ScrollState) {
}
@Composable
private fun PasswordInfo() {
fun PasswordInfo(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.password_info), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.password_info, 8.dp, navCtrl) {
if(VERSION.SDK_INT >= 29) {
val passwordComplexity = mapOf(
PASSWORD_COMPLEXITY_NONE to R.string.password_complexity_none,
@@ -293,17 +237,14 @@ private fun PasswordInfo() {
@SuppressLint("NewApi")
@Composable
private fun ResetPasswordToken() {
fun ResetPasswordToken(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
var token by remember { mutableStateOf("") }
val tokenByteArray = token.toByteArray()
val focusMgr = LocalFocusManager.current
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.reset_password_token), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.reset_password_token, 8.dp, navCtrl) {
OutlinedTextField(
value = token, onValueChange = { token = it },
label = { Text(stringResource(R.string.token)) },
@@ -367,7 +308,7 @@ private fun ResetPasswordToken() {
}
@Composable
private fun ResetPassword() {
fun ResetPassword(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -378,10 +319,7 @@ private fun ResetPassword() {
val tokenByteArray = token.toByteArray()
val flags = remember { mutableStateListOf<Int>() }
var confirmDialog by remember { mutableStateOf(false) }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.reset_password),style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.reset_password, 8.dp, navCtrl) {
if(VERSION.SDK_INT >= 26) {
OutlinedTextField(
value = token, onValueChange = { token = it },
@@ -444,7 +382,6 @@ private fun ResetPassword() {
}
}
InfoCard(R.string.info_reset_password)
Spacer(Modifier.padding(vertical = 30.dp))
}
if(confirmDialog) {
var confirmPassword by remember { mutableStateOf("") }
@@ -494,7 +431,7 @@ private fun ResetPassword() {
@SuppressLint("NewApi")
@Composable
private fun PasswordComplexity() {
fun PasswordComplexity(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val passwordComplexity = mapOf(
@@ -502,19 +439,12 @@ private fun PasswordComplexity() {
PASSWORD_COMPLEXITY_LOW to R.string.password_complexity_low,
PASSWORD_COMPLEXITY_MEDIUM to R.string.password_complexity_medium,
PASSWORD_COMPLEXITY_HIGH to R.string.password_complexity_high
).toList()
var selectedItem by remember { mutableIntStateOf(passwordComplexity[0].first) }
)
var selectedItem by remember { mutableIntStateOf(PASSWORD_COMPLEXITY_NONE) }
LaunchedEffect(Unit) { selectedItem = dpm.requiredPasswordComplexity }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.required_password_complexity), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
for(index in 0..3) {
RadioButtonItem(
passwordComplexity[index].second,
selectedItem == passwordComplexity[index].first,
{ selectedItem = passwordComplexity[index].first }
)
MyScaffold(R.string.required_password_complexity, 8.dp, navCtrl) {
passwordComplexity.forEach {
RadioButtonItem(it.value, selectedItem == it.key, { selectedItem = it.key })
}
Spacer(Modifier.padding(vertical = 5.dp))
Button(
@@ -533,12 +463,11 @@ private fun PasswordComplexity() {
) {
Text(stringResource(R.string.require_set_new_password))
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@Composable
private fun DisableKeyguardFeatures() {
fun DisableKeyguardFeatures(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -579,10 +508,7 @@ private fun DisableKeyguardFeatures() {
}
calculateCustomFeature()
}
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.disable_keyguard_features), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.disable_keyguard_features, 8.dp, navCtrl) {
RadioButtonItem(R.string.enable_all, state == 0, { state = 0 })
RadioButtonItem(R.string.disable_all, state == 1, { state = 1 })
RadioButtonItem(R.string.custom, state == 2 , { state = 2 })
@@ -630,12 +556,11 @@ private fun DisableKeyguardFeatures() {
) {
Text(text = stringResource(R.string.apply))
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@Composable
private fun PasswordQuality() {
fun PasswordQuality(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -647,15 +572,12 @@ private fun PasswordQuality() {
PASSWORD_QUALITY_ALPHANUMERIC to R.string.password_quality_alphanumeric,
PASSWORD_QUALITY_BIOMETRIC_WEAK to R.string.password_quality_biometrics_weak,
PASSWORD_QUALITY_NUMERIC_COMPLEX to R.string.password_quality_numeric_complex
).toList()
var selectedItem by remember { mutableIntStateOf(passwordQuality[0].first) }
)
var selectedItem by remember { mutableIntStateOf(PASSWORD_QUALITY_UNSPECIFIED) }
LaunchedEffect(Unit) { selectedItem=dpm.getPasswordQuality(receiver) }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.required_password_quality), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
for(index in 1..6) {
RadioButtonItem(passwordQuality[index].second, selectedItem == passwordQuality[index].first, { selectedItem = passwordQuality[index].first })
MyScaffold(R.string.required_password_quality, 8.dp, navCtrl) {
passwordQuality.forEach {
RadioButtonItem(it.value, selectedItem == it.key, { selectedItem = it.key })
}
Spacer(Modifier.padding(vertical = 5.dp))
Button(
@@ -667,7 +589,6 @@ private fun PasswordQuality() {
) {
Text(stringResource(R.string.apply))
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}

View File

@@ -13,7 +13,6 @@ import android.os.RemoteException
import android.os.UserManager
import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
@@ -23,7 +22,6 @@ import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
@@ -31,10 +29,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.backToHomeStateFlow
import com.bintianqi.owndroid.ui.*
@@ -44,47 +38,9 @@ import com.rosan.dhizuku.api.Dhizuku
import com.rosan.dhizuku.api.DhizukuRequestPermissionListener
import kotlinx.coroutines.launch
@Composable
fun DpmPermissions(navCtrl:NavHostController) {
val localNavCtrl = rememberNavController()
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
val scrollState = rememberScrollState()
Scaffold(
topBar = {
TopBar(backStackEntry,navCtrl,localNavCtrl) {
if(backStackEntry?.destination?.route=="Home"&&scrollState.maxValue > 100) {
Text(
text = stringResource(R.string.permission),
modifier = Modifier.alpha((maxOf(scrollState.value-30,0)).toFloat()/80)
)
}
}
}
) {
NavHost(
navController = localNavCtrl, startDestination = "Home",
enterTransition = Animations.navHostEnterTransition,
exitTransition = Animations.navHostExitTransition,
popEnterTransition = Animations.navHostPopEnterTransition,
popExitTransition = Animations.navHostPopExitTransition,
modifier = Modifier.padding(top = it.calculateTopPadding())
) {
composable(route = "Home") { Home(localNavCtrl,scrollState) }
composable(route = "Shizuku") { ShizukuActivate() }
composable(route = "DeviceAdmin") { DeviceAdmin() }
composable(route = "ProfileOwner") { ProfileOwner() }
composable(route = "DeviceOwner") { DeviceOwner() }
composable(route = "DeviceInfo") { DeviceInfo() }
composable(route = "LockScreenInfo") { LockScreenInfo() }
composable(route = "SupportMsg") { SupportMsg() }
composable(route = "TransformOwnership") { TransferOwnership() }
}
}
}
@SuppressLint("NewApi")
@Composable
private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) {
fun Permissions(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -95,12 +51,7 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) {
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
var dialog by remember { mutableIntStateOf(0) }
val enrollmentSpecificId = if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) dpm.enrollmentSpecificId else ""
Column(modifier = Modifier.fillMaxSize().verticalScroll(listScrollState)) {
Text(
text = stringResource(R.string.permission),
style = typography.headlineLarge,
modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 16.dp)
)
MyScaffold(R.string.permissions, 0.dp, navCtrl) {
if(!dpm.isDeviceOwnerApp(context.packageName)) {
SwitchItem(
R.string.dhizuku, "", null,
@@ -109,43 +60,42 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) {
onClickBlank = { dialog = 4 }
)
}
SubPageItem(
FunctionItem(
R.string.device_admin, stringResource(if(deviceAdmin) R.string.activated else R.string.deactivated),
operation = { localNavCtrl.navigate("DeviceAdmin") }
operation = { navCtrl.navigate("DeviceAdmin") }
)
if(profileOwner || !userManager.isSystemUser) {
SubPageItem(
FunctionItem(
R.string.profile_owner, stringResource(if(profileOwner) R.string.activated else R.string.deactivated),
operation = { localNavCtrl.navigate("ProfileOwner") }
operation = { navCtrl.navigate("ProfileOwner") }
)
}
if(!profileOwner && userManager.isSystemUser) {
SubPageItem(
FunctionItem(
R.string.device_owner, stringResource(if(deviceOwner) R.string.activated else R.string.deactivated),
operation = { localNavCtrl.navigate("DeviceOwner") }
operation = { navCtrl.navigate("DeviceOwner") }
)
}
SubPageItem(R.string.shizuku,"") { localNavCtrl.navigate("Shizuku") }
SubPageItem(R.string.device_info, "", R.drawable.perm_device_information_fill0) { localNavCtrl.navigate("DeviceInfo") }
FunctionItem(R.string.shizuku,"") { navCtrl.navigate("Shizuku") }
FunctionItem(R.string.device_info, "", R.drawable.perm_device_information_fill0) { navCtrl.navigate("DeviceInfo") }
if((VERSION.SDK_INT >= 26 && deviceOwner) || (VERSION.SDK_INT>=24 && profileOwner)) {
SubPageItem(R.string.org_name, "", R.drawable.corporate_fare_fill0) { dialog = 2 }
FunctionItem(R.string.org_name, "", R.drawable.corporate_fare_fill0) { dialog = 2 }
}
if(VERSION.SDK_INT >= 31 && (profileOwner || deviceOwner)) {
SubPageItem(R.string.org_id, "", R.drawable.corporate_fare_fill0) { dialog = 3 }
FunctionItem(R.string.org_id, "", R.drawable.corporate_fare_fill0) { dialog = 3 }
}
if(enrollmentSpecificId != "") {
SubPageItem(R.string.enrollment_specific_id, "", R.drawable.id_card_fill0) { dialog = 1 }
FunctionItem(R.string.enrollment_specific_id, "", R.drawable.id_card_fill0) { dialog = 1 }
}
if(VERSION.SDK_INT >= 24 && (deviceOwner || dpm.isOrgProfile(receiver))) {
SubPageItem(R.string.device_owner_lock_screen_info, "", R.drawable.screen_lock_portrait_fill0) { localNavCtrl.navigate("LockScreenInfo") }
FunctionItem(R.string.lock_screen_info, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("LockScreenInfo") }
}
if(VERSION.SDK_INT >= 24 && deviceAdmin) {
SubPageItem(R.string.support_msg, "", R.drawable.chat_fill0) { localNavCtrl.navigate("SupportMsg") }
FunctionItem(R.string.support_messages, "", R.drawable.chat_fill0) { navCtrl.navigate("SupportMessages") }
}
if(VERSION.SDK_INT >= 28 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.transfer_ownership, "", R.drawable.admin_panel_settings_fill0) { localNavCtrl.navigate("TransformOwnership") }
FunctionItem(R.string.transfer_ownership, "", R.drawable.admin_panel_settings_fill0) { navCtrl.navigate("TransferOwnership") }
}
Spacer(Modifier.padding(vertical = 30.dp))
}
if(dialog != 0) {
var input by remember { mutableStateOf("") }
@@ -157,7 +107,7 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) {
2 -> R.string.org_name
3 -> R.string.org_id
4 -> R.string.dhizuku
else -> R.string.permission
else -> R.string.permissions
}
))
},
@@ -176,7 +126,7 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) {
1 -> R.string.enrollment_specific_id
2 -> R.string.org_name
3 -> R.string.org_id
else -> R.string.permission
else -> R.string.permissions
}
))
},
@@ -237,7 +187,7 @@ private fun toggleDhizukuMode(status: Boolean, context: Context) {
dhizukuErrorStatus.value = 1
return
}
if(Dhizuku.isPermissionGranted()) {
if(dhizukuPermissionGranted()) {
sharedPref.edit().putBoolean("dhizuku", true).apply()
Dhizuku.init(context)
backToHomeStateFlow.value = true
@@ -260,17 +210,16 @@ private fun toggleDhizukuMode(status: Boolean, context: Context) {
@SuppressLint("NewApi")
@Composable
private fun LockScreenInfo() {
fun LockScreenInfo(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
var infoText by remember { mutableStateOf(dpm.deviceOwnerLockScreenInfo?.toString() ?: "") }
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) {
Text(text = stringResource(R.string.device_owner_lock_screen_info), style = typography.headlineLarge)
MyScaffold(R.string.lock_screen_info, 8.dp, navCtrl) {
OutlinedTextField(
value = infoText,
label = { Text(stringResource(R.string.device_owner_lock_screen_info)) },
label = { Text(stringResource(R.string.lock_screen_info)) },
onValueChange = { infoText = it },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions { focusMgr.clearFocus() },
@@ -302,15 +251,13 @@ private fun LockScreenInfo() {
}
@Composable
private fun DeviceAdmin() {
fun DeviceAdmin(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
var deactivateDialog by remember { mutableStateOf(false) }
val deviceAdmin = context.isDeviceAdmin
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.device_admin), style = typography.headlineLarge)
MyScaffold(R.string.device_admin, 8.dp, navCtrl) {
Text(text = stringResource(if(context.isDeviceAdmin) R.string.activated else R.string.deactivated), style = typography.titleLarge)
Spacer(Modifier.padding(vertical = 5.dp))
AnimatedVisibility(deviceAdmin) {
@@ -361,15 +308,13 @@ private fun DeviceAdmin() {
}
@Composable
private fun ProfileOwner() {
fun ProfileOwner(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
var deactivateDialog by remember { mutableStateOf(false) }
val profileOwner = context.isProfileOwner
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.profile_owner), style = typography.headlineLarge)
MyScaffold(R.string.profile_owner, 8.dp, navCtrl) {
Text(stringResource(if(profileOwner) R.string.activated else R.string.deactivated), style = typography.titleLarge)
Spacer(Modifier.padding(vertical = 5.dp))
if(VERSION.SDK_INT >= 24 && profileOwner) {
@@ -416,14 +361,12 @@ private fun ProfileOwner() {
}
@Composable
private fun DeviceOwner() {
fun DeviceOwner(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
var deactivateDialog by remember { mutableStateOf(false) }
val deviceOwner = context.isDeviceOwner
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.device_owner), style = typography.headlineLarge)
MyScaffold(R.string.device_owner, 8.dp, navCtrl) {
Text(text = stringResource(if(deviceOwner) R.string.activated else R.string.deactivated), style = typography.titleLarge)
Spacer(Modifier.padding(vertical = 5.dp))
AnimatedVisibility(deviceOwner) {
@@ -488,15 +431,12 @@ private fun DeviceOwner() {
}
@Composable
fun DeviceInfo() {
fun DeviceInfo(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
var dialog by remember { mutableIntStateOf(0) }
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.device_info), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.device_info, 8.dp, navCtrl) {
if(VERSION.SDK_INT>=34 && (context.isDeviceOwner || dpm.isOrgProfile(receiver))) {
CardItem(R.string.financed_device, dpm.isDeviceFinanced.yesOrNo)
}
@@ -532,7 +472,7 @@ fun DeviceInfo() {
@SuppressLint("NewApi")
@Composable
private fun SupportMsg() {
fun SupportMessages(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -543,10 +483,7 @@ private fun SupportMsg() {
longMsg = dpm.getLongSupportMessage(receiver)?.toString() ?: ""
}
LaunchedEffect(Unit) { refreshMsg() }
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.support_msg), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.support_messages, 8.dp, navCtrl) {
OutlinedTextField(
value = shortMsg,
label = { Text(stringResource(R.string.short_support_msg)) },
@@ -608,22 +545,18 @@ private fun SupportMsg() {
}
}
InfoCard(R.string.info_long_support_message)
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@SuppressLint("NewApi")
@Composable
private fun TransferOwnership() {
fun TransferOwnership(navCtrl: NavHostController) {
val context = LocalContext.current
val focusMgr = LocalFocusManager.current
var input by remember { mutableStateOf("") }
val componentName = ComponentName.unflattenFromString(input)
var dialog by remember { mutableStateOf(false) }
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.transfer_ownership), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.transfer_ownership, 8.dp, navCtrl) {
OutlinedTextField(
value = input, onValueChange = { input = it }, label = { Text(stringResource(R.string.target_component_name)) },
modifier = Modifier.fillMaxWidth(),

View File

@@ -10,14 +10,12 @@ import android.os.IBinder
import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -34,8 +32,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import com.bintianqi.owndroid.IUserService
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.ui.MyScaffold
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import rikka.shizuku.Shizuku
@@ -43,7 +43,7 @@ import rikka.shizuku.Shizuku
private var waitGrantPermission = false
@Composable
fun ShizukuActivate() {
fun Shizuku(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -69,19 +69,14 @@ fun ShizukuActivate() {
shizukuService.value = null
userServiceControl(context, true)
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 8.dp)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) {
AnimatedVisibility(bindShizuku) {
MyScaffold(R.string.shizuku, 0.dp, navCtrl, false) {
AnimatedVisibility(visible = bindShizuku, modifier = Modifier.fillMaxWidth()) {
Button(
onClick = {
userServiceControl(context, true)
outputText = ""
}
},
modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally)
) {
Text(stringResource(R.string.bind_shizuku))
}
@@ -100,7 +95,8 @@ fun ShizukuActivate() {
coScope.launch {
outputTextScrollState.animateScrollTo(0)
}
}
},
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
Text(text = stringResource(R.string.check_shizuku))
}
@@ -112,7 +108,8 @@ fun ShizukuActivate() {
outputTextScrollState.animateScrollTo(0)
}
},
enabled = enabled
enabled = enabled,
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
Text(text = stringResource(R.string.list_owners))
}
@@ -123,7 +120,8 @@ fun ShizukuActivate() {
outputTextScrollState.animateScrollTo(0)
}
},
enabled = enabled
enabled = enabled,
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
Text(text = stringResource(R.string.list_users))
}
@@ -134,7 +132,8 @@ fun ShizukuActivate() {
outputTextScrollState.animateScrollTo(0)
}
},
enabled = enabled
enabled = enabled,
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
Text(text = stringResource(R.string.list_accounts))
}
@@ -150,7 +149,8 @@ fun ShizukuActivate() {
showDeviceAdminButton = !context.isDeviceAdmin
}
},
enabled = enabled
enabled = enabled,
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
Text(text = stringResource(R.string.activate_device_admin))
}
@@ -166,7 +166,8 @@ fun ShizukuActivate() {
showDeviceOwnerButton = !context.isDeviceOwner
}
},
enabled = enabled
enabled = enabled,
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
Text(text = stringResource(R.string.activate_device_owner))
}
@@ -186,7 +187,8 @@ fun ShizukuActivate() {
showOrgProfileOwnerButton = !dpm.isOrganizationOwnedDeviceWithManagedProfile
}
},
enabled = enabled
enabled = enabled,
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
Text(text = stringResource(R.string.activate_org_profile))
}
@@ -194,10 +196,8 @@ fun ShizukuActivate() {
}
SelectionContainer(modifier = Modifier.fillMaxWidth().horizontalScroll(outputTextScrollState)) {
Text(text = outputText, softWrap = false, modifier = Modifier.padding(4.dp))
Text(text = outputText, softWrap = false, modifier = Modifier.padding(vertical = 9.dp, horizontal = 12.dp))
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}

View File

@@ -44,23 +44,19 @@ import android.os.UserManager
import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.List
import androidx.compose.material.icons.filled.Add
@@ -72,7 +68,6 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
@@ -90,7 +85,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
@@ -101,10 +95,6 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.core.app.NotificationCompat
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.StopLockTaskModeReceiver
import com.bintianqi.owndroid.exportFile
@@ -115,15 +105,14 @@ import com.bintianqi.owndroid.getFile
import com.bintianqi.owndroid.prepareForNotification
import com.bintianqi.owndroid.selectedPackage
import com.bintianqi.owndroid.toggle
import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard
import com.bintianqi.owndroid.ui.Information
import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.ui.SubPageItem
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.TopBar
import com.bintianqi.owndroid.uriToStream
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -137,54 +126,9 @@ import java.util.TimeZone
import java.util.concurrent.Executors
import kotlin.math.pow
@Composable
fun SystemManage(navCtrl: NavHostController) {
val localNavCtrl = rememberNavController()
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
val scrollState = rememberScrollState()
Scaffold(
topBar = {
TopBar(backStackEntry,navCtrl,localNavCtrl) {
if(backStackEntry?.destination?.route=="Home"&&scrollState.maxValue > 100) {
Text(
text = stringResource(R.string.system),
modifier = Modifier.alpha((maxOf(scrollState.value-30,0)).toFloat()/80)
)
}
}
}
) {
NavHost(
navController = localNavCtrl, startDestination = "Home",
enterTransition = Animations.navHostEnterTransition,
exitTransition = Animations.navHostExitTransition,
popEnterTransition = Animations.navHostPopEnterTransition,
popExitTransition = Animations.navHostPopExitTransition,
modifier = Modifier.padding(top = it.calculateTopPadding())
) {
composable(route = "Home") { Home(localNavCtrl, scrollState) }
composable(route = "Switches") { Switches() }
composable(route = "Keyguard") { Keyguard() }
composable(route = "EditTime") { EditTime() }
composable(route = "EditTimeZone") { EditTimeZone() }
composable(route = "PermissionPolicy") { PermissionPolicy() }
composable(route = "MTEPolicy") { MTEPolicy() }
composable(route = "NearbyStreamingPolicy") { NearbyStreamingPolicy() }
composable(route = "LockTaskMode") { LockTaskMode(navCtrl) }
composable(route = "CaCert") { CaCert() }
composable(route = "SecurityLogs") { SecurityLogs() }
composable(route = "DisableAccountManagement") { DisableAccountManagement() }
composable(route = "SystemUpdatePolicy") { SysUpdatePolicy() }
composable(route = "InstallSystemUpdate") { InstallSystemUpdate() }
composable(route = "WipeData") { WipeData() }
composable(route = "FRP") { FactoryResetProtection() }
}
}
}
@SuppressLint("NewApi")
@Composable
private fun Home(navCtrl: NavHostController, scrollState: ScrollState) {
fun SystemManage(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -194,60 +138,54 @@ private fun Home(navCtrl: NavHostController, scrollState: ScrollState) {
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
var dialog by remember { mutableIntStateOf(0) }
Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) {
Text(
text = stringResource(R.string.system),
style = typography.headlineLarge,
modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 16.dp)
)
MyScaffold(R.string.system, 0.dp, navCtrl) {
if(deviceOwner || profileOwner) {
SubPageItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Switches") }
FunctionItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("SystemOptions") }
}
SubPageItem(R.string.keyguard, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("Keyguard") }
FunctionItem(R.string.keyguard, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("Keyguard") }
if(VERSION.SDK_INT >= 24 && deviceOwner) {
SubPageItem(R.string.reboot, "", R.drawable.restart_alt_fill0) { dialog = 1 }
FunctionItem(R.string.reboot, "", R.drawable.restart_alt_fill0) { dialog = 1 }
}
if(deviceOwner && ((VERSION.SDK_INT >= 28 && dpm.isAffiliatedUser) || VERSION.SDK_INT >= 24)) {
SubPageItem(R.string.bug_report, "", R.drawable.bug_report_fill0) { dialog = 2 }
FunctionItem(R.string.bug_report, "", R.drawable.bug_report_fill0) { dialog = 2 }
}
if(VERSION.SDK_INT >= 28 && (deviceOwner || dpm.isOrgProfile(receiver))) {
SubPageItem(R.string.change_time, "", R.drawable.schedule_fill0) { navCtrl.navigate("EditTime") }
SubPageItem(R.string.change_timezone, "", R.drawable.schedule_fill0) { navCtrl.navigate("EditTimeZone") }
FunctionItem(R.string.change_time, "", R.drawable.schedule_fill0) { navCtrl.navigate("ChangeTime") }
FunctionItem(R.string.change_timezone, "", R.drawable.schedule_fill0) { navCtrl.navigate("ChangeTimeZone") }
}
if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.permission_policy, "", R.drawable.key_fill0) { navCtrl.navigate("PermissionPolicy") }
FunctionItem(R.string.permission_policy, "", R.drawable.key_fill0) { navCtrl.navigate("PermissionPolicy") }
}
if(VERSION.SDK_INT >= 34 && deviceOwner) {
SubPageItem(R.string.mte_policy, "", R.drawable.memory_fill0) { navCtrl.navigate("MTEPolicy") }
FunctionItem(R.string.mte_policy, "", R.drawable.memory_fill0) { navCtrl.navigate("MTEPolicy") }
}
if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.nearby_streaming_policy, "", R.drawable.share_fill0) { navCtrl.navigate("NearbyStreamingPolicy") }
FunctionItem(R.string.nearby_streaming_policy, "", R.drawable.share_fill0) { navCtrl.navigate("NearbyStreamingPolicy") }
}
if(VERSION.SDK_INT >= 28 && deviceOwner) {
SubPageItem(R.string.lock_task_mode, "", R.drawable.lock_fill0) { navCtrl.navigate("LockTaskMode") }
FunctionItem(R.string.lock_task_mode, "", R.drawable.lock_fill0) { navCtrl.navigate("LockTaskMode") }
}
if(deviceOwner || profileOwner) {
SubPageItem(R.string.ca_cert, "", R.drawable.license_fill0) { navCtrl.navigate("CaCert") }
FunctionItem(R.string.ca_cert, "", R.drawable.license_fill0) { navCtrl.navigate("CACert") }
}
if(VERSION.SDK_INT >= 26 && !dhizuku && (deviceOwner || dpm.isOrgProfile(receiver))) {
SubPageItem(R.string.security_logs, "", R.drawable.description_fill0) { navCtrl.navigate("SecurityLogs") }
FunctionItem(R.string.security_logging, "", R.drawable.description_fill0) { navCtrl.navigate("SecurityLogging") }
}
if(deviceOwner || profileOwner) {
SubPageItem(R.string.disable_account_management, "", R.drawable.account_circle_fill0) { navCtrl.navigate("DisableAccountManagement") }
FunctionItem(R.string.disable_account_management, "", R.drawable.account_circle_fill0) { navCtrl.navigate("DisableAccountManagement") }
}
if(VERSION.SDK_INT >= 23 && (deviceOwner || dpm.isOrgProfile(receiver))) {
SubPageItem(R.string.system_update_policy, "", R.drawable.system_update_fill0) { navCtrl.navigate("SystemUpdatePolicy") }
FunctionItem(R.string.system_update_policy, "", R.drawable.system_update_fill0) { navCtrl.navigate("SystemUpdatePolicy") }
}
if(VERSION.SDK_INT >= 29 && (deviceOwner || dpm.isOrgProfile(receiver))) {
SubPageItem(R.string.install_system_update, "", R.drawable.system_update_fill0) { navCtrl.navigate("InstallSystemUpdate") }
FunctionItem(R.string.install_system_update, "", R.drawable.system_update_fill0) { navCtrl.navigate("InstallSystemUpdate") }
}
if(VERSION.SDK_INT >= 30 && (deviceOwner || dpm.isOrgProfile(receiver))) {
SubPageItem(R.string.frp_policy, "", R.drawable.device_reset_fill0) { navCtrl.navigate("FRP") }
FunctionItem(R.string.frp_policy, "", R.drawable.device_reset_fill0) { navCtrl.navigate("FRPPolicy") }
}
if(dangerousFeatures && context.isDeviceAdmin && !(VERSION.SDK_INT >= 24 && profileOwner && dpm.isManagedProfile(receiver))) {
SubPageItem(R.string.wipe_data, "", R.drawable.device_reset_fill0) { navCtrl.navigate("WipeData") }
FunctionItem(R.string.wipe_data, "", R.drawable.device_reset_fill0) { navCtrl.navigate("WipeData") }
}
Spacer(Modifier.padding(vertical = 30.dp))
LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") }
}
if(dialog != 0) AlertDialog(
@@ -279,7 +217,7 @@ private fun Home(navCtrl: NavHostController, scrollState: ScrollState) {
}
@Composable
private fun Switches() {
fun SystemOptions(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -287,8 +225,7 @@ private fun Switches() {
val profileOwner = context.isProfileOwner
val um = context.getSystemService(Context.USER_SERVICE) as UserManager
var dialog by remember { mutableIntStateOf(0) }
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.options, 0.dp, navCtrl) {
if(deviceOwner || profileOwner) {
SwitchItem(R.string.disable_cam,"", R.drawable.photo_camera_fill0,
{ dpm.getCameraDisabled(null) }, { dpm.setCameraDisabled(receiver,it) }
@@ -344,7 +281,6 @@ private fun Switches() {
{ dpm.isUsbDataSignalingEnabled = !it },
)
}
Spacer(Modifier.padding(vertical = 30.dp))
}
if(dialog != 0) AlertDialog(
text = {
@@ -364,17 +300,14 @@ private fun Switches() {
}
@Composable
private fun Keyguard() {
fun Keyguard(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
MyScaffold(R.string.keyguard, 8.dp, navCtrl) {
if(VERSION.SDK_INT >= 23) {
Text(text = stringResource(R.string.keyguard), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
@@ -401,7 +334,7 @@ private fun Keyguard() {
InfoCard(R.string.info_disable_keyguard)
Spacer(Modifier.padding(vertical = 12.dp))
}
Text(text = stringResource(R.string.lock_now), style = typography.headlineLarge)
if(VERSION.SDK_INT >= 23) Text(text = stringResource(R.string.lock_now), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 2.dp))
var flag by remember { mutableIntStateOf(0) }
if(VERSION.SDK_INT >= 26 && profileOwner && dpm.isManagedProfile(receiver)) {
@@ -424,22 +357,18 @@ private fun Keyguard() {
if(VERSION.SDK_INT >= 26 && profileOwner && dpm.isManagedProfile(receiver)) {
InfoCard(R.string.info_evict_credential_encryption_key)
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@SuppressLint("NewApi")
@Composable
private fun EditTime() {
fun ChangeTime(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
var inputTime by remember { mutableStateOf("") }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.change_time), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.change_time, 8.dp, navCtrl) {
OutlinedTextField(
value = inputTime,
label = { Text(stringResource(R.string.time_unit_ms)) },
@@ -459,22 +388,17 @@ private fun EditTime() {
}
}
}
@SuppressLint("NewApi")
@Composable
private fun EditTimeZone() {
fun ChangeTimeZone(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val focusMgr = LocalFocusManager.current
val receiver = context.getReceiver()
var inputTimezone by remember { mutableStateOf("") }
var dialog by remember { mutableStateOf(false) }
Column(
modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.change_timezone), style = typography.headlineLarge, modifier = Modifier.align(Alignment.Start))
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.change_timezone, 8.dp, navCtrl) {
OutlinedTextField(
value = inputTimezone,
label = { Text(stringResource(R.string.timezone_id)) },
@@ -533,15 +457,12 @@ private fun EditTimeZone() {
@SuppressLint("NewApi")
@Composable
private fun PermissionPolicy() {
fun PermissionPolicy(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
var selectedPolicy by remember { mutableIntStateOf(dpm.getPermissionPolicy(receiver)) }
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.permission_policy), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
var selectedPolicy by remember { mutableIntStateOf(dpm.getPermissionPolicy(receiver)) }
MyScaffold(R.string.permission_policy, 8.dp, navCtrl) {
RadioButtonItem(R.string.default_stringres, selectedPolicy == PERMISSION_POLICY_PROMPT, { selectedPolicy = PERMISSION_POLICY_PROMPT })
RadioButtonItem(R.string.auto_grant, selectedPolicy == PERMISSION_POLICY_AUTO_GRANT, { selectedPolicy = PERMISSION_POLICY_AUTO_GRANT })
RadioButtonItem(R.string.auto_deny, selectedPolicy == PERMISSION_POLICY_AUTO_DENY, { selectedPolicy = PERMISSION_POLICY_AUTO_DENY })
@@ -561,14 +482,11 @@ private fun PermissionPolicy() {
@SuppressLint("NewApi")
@Composable
private fun MTEPolicy() {
fun MTEPolicy(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.mte_policy), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
var selectedMtePolicy by remember { mutableIntStateOf(dpm.mtePolicy) }
var selectedMtePolicy by remember { mutableIntStateOf(dpm.mtePolicy) }
MyScaffold(R.string.mte_policy, 8.dp, navCtrl) {
RadioButtonItem(
R.string.decide_by_user,
selectedMtePolicy == MTE_NOT_CONTROLLED_BY_POLICY,
@@ -599,17 +517,16 @@ private fun MTEPolicy() {
Text(stringResource(R.string.apply))
}
InfoCard(R.string.info_mte_policy)
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@SuppressLint("NewApi")
@Composable
private fun NearbyStreamingPolicy() {
fun NearbyStreamingPolicy(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
var appPolicy by remember { mutableIntStateOf(dpm.nearbyAppStreamingPolicy) }
var appPolicy by remember { mutableIntStateOf(dpm.nearbyAppStreamingPolicy) }
MyScaffold(R.string.nearby_streaming_policy, 8.dp, navCtrl) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.nearby_app_streaming), style = typography.titleLarge)
Spacer(Modifier.padding(vertical = 3.dp))
@@ -679,20 +596,19 @@ private fun NearbyStreamingPolicy() {
Text(stringResource(R.string.apply))
}
InfoCard(R.string.info_nearby_notification_streaming_policy)
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@SuppressLint("NewApi")
@Composable
private fun LockTaskMode(navCtrl: NavHostController) {
fun LockTaskMode(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
val coroutine = rememberCoroutineScope()
var appSelectorRequest by rememberSaveable { mutableIntStateOf(0) }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
MyScaffold(R.string.lock_task_mode, 8.dp, navCtrl, false) {
val lockTaskFeatures = remember { mutableStateListOf<Int>() }
var custom by rememberSaveable { mutableStateOf(false) }
val refreshFeature = {
@@ -710,7 +626,7 @@ private fun LockTaskMode(navCtrl: NavHostController) {
}
if(calculate - 1 >= 0) { lockTaskFeatures += 1 }
custom = true
}else{
} else {
custom = false
}
}
@@ -803,7 +719,7 @@ private fun LockTaskMode(navCtrl: NavHostController) {
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
trailingIcon = {
Icon(painter = painterResource(R.drawable.checklist_fill0), contentDescription = null,
Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null,
modifier = Modifier
.clip(RoundedCornerShape(50))
.clickable(onClick = {
@@ -865,7 +781,7 @@ private fun LockTaskMode(navCtrl: NavHostController) {
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
trailingIcon = {
Icon(painter = painterResource(R.drawable.checklist_fill0), contentDescription = null,
Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null,
modifier = Modifier
.clip(RoundedCornerShape(50))
.clickable(onClick = {
@@ -915,12 +831,11 @@ private fun LockTaskMode(navCtrl: NavHostController) {
Text(stringResource(R.string.start))
}
InfoCard(R.string.info_start_lock_task_mode)
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@Composable
private fun CaCert() {
fun CACert(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -941,10 +856,7 @@ private fun CaCert() {
exist = dpm.hasCaCertInstalled(receiver, caCertByteArray)
}
}
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.ca_cert), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.ca_cert, 8.dp, navCtrl) {
AnimatedVisibility(uriPath != "") {
Text(text = uriPath)
}
@@ -1004,7 +916,7 @@ private fun CaCert() {
@SuppressLint("NewApi")
@Composable
private fun SecurityLogs() {
fun SecurityLogging(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -1013,10 +925,7 @@ private fun SecurityLogs() {
LaunchedEffect(Unit) {
fileSize = logFile.length()
}
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.security_logs), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.security_logging, 8.dp, navCtrl) {
SwitchItem(R.string.enable, "", null, { dpm.isSecurityLoggingEnabled(receiver) }, { dpm.setSecurityLoggingEnabled(receiver, it) }, padding = false)
Text(stringResource(R.string.log_file_size_is, formatFileSize(fileSize)))
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
@@ -1087,21 +996,18 @@ private fun SecurityLogs() {
}
@Composable
private fun DisableAccountManagement() {
fun DisableAccountManagement(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.disable_account_management), style = typography.headlineLarge)
MyScaffold(R.string.disable_account_management, 8.dp, navCtrl) {
val list = remember { mutableStateListOf<String>() }
fun refreshList() {
list.clear()
dpm.accountTypesWithManagementDisabled?.forEach { list += it }
}
LaunchedEffect(Unit) { refreshList() }
Spacer(Modifier.padding(vertical = 5.dp))
Column(modifier = Modifier.animateContentSize()) {
if(list.isEmpty()) Text(stringResource(R.string.none))
for(i in list) {
@@ -1139,7 +1045,7 @@ private fun DisableAccountManagement() {
@SuppressLint("NewApi")
@Composable
fun FactoryResetProtection() {
fun FRPPolicy(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val focusMgr = LocalFocusManager.current
@@ -1165,12 +1071,7 @@ fun FactoryResetProtection() {
}
}
}
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.frp_policy), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 3.dp))
Text(stringResource(R.string.factory_reset_protection_policy))
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.frp_policy, 8.dp, navCtrl) {
Row(
horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().padding(horizontal = 6.dp, vertical = 8.dp)
@@ -1231,13 +1132,12 @@ fun FactoryResetProtection() {
if(unsupported) Text(stringResource(R.string.frp_policy_not_supported))
Spacer(Modifier.padding(vertical = 6.dp))
InfoCard(R.string.info_frp_policy)
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@SuppressLint("NewApi")
@Composable
private fun WipeData() {
fun WipeData(navCtrl: NavHostController) {
val context = LocalContext.current
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
val dpm = context.getDPM()
@@ -1249,14 +1149,7 @@ private fun WipeData() {
var euicc by remember { mutableStateOf(false) }
var silent by remember { mutableStateOf(false) }
var reason by remember { mutableStateOf("") }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(
text = stringResource(R.string.wipe_data),
style = typography.headlineLarge,
modifier = Modifier.padding(6.dp),color = colorScheme.error
)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.wipe_data, 8.dp, navCtrl) {
CheckBoxItem(
R.string.wipe_external_storage,
externalStorage, { externalStorage = it }
@@ -1302,7 +1195,6 @@ private fun WipeData() {
Text("WipeDevice")
}
}
Spacer(Modifier.padding(vertical = 30.dp))
}
if(warning) {
LaunchedEffect(Unit) { silent = reason == "" }
@@ -1360,18 +1252,15 @@ private fun WipeData() {
}
@Composable
private fun SysUpdatePolicy() {
fun SystemUpdatePolicy(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
MyScaffold(R.string.system_update_policy, 8.dp, navCtrl) {
if(VERSION.SDK_INT >= 23) {
Column {
var selectedPolicy by remember { mutableStateOf(dpm.systemUpdatePolicy?.policyType) }
Text(text = stringResource(R.string.system_update_policy), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
RadioButtonItem(
R.string.system_update_policy_automatic,
selectedPolicy == TYPE_INSTALL_AUTOMATIC, { selectedPolicy = TYPE_INSTALL_AUTOMATIC }
@@ -1447,13 +1336,12 @@ private fun SysUpdatePolicy() {
}
}
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@SuppressLint("NewApi")
@Composable
fun InstallSystemUpdate() {
fun InstallSystemUpdate(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -1472,10 +1360,7 @@ fun InstallSystemUpdate() {
}
}
val uri by fileUriFlow.collectAsState()
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.install_system_update), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.install_system_update, 8.dp, navCtrl) {
Button(
onClick = {
val intent = Intent(Intent.ACTION_GET_CONTENT)
@@ -1508,10 +1393,7 @@ fun InstallSystemUpdate() {
}
}
Spacer(Modifier.padding(vertical = 10.dp))
Information {
Text(stringResource(R.string.auto_reboot_after_install_succeed))
}
Spacer(Modifier.padding(vertical = 30.dp))
InfoCard(R.string.auto_reboot_after_install_succeed)
}
}

View File

@@ -6,34 +6,20 @@ import android.os.UserManager
import android.widget.Toast
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.SubPageItem
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.TopBar
data class Restriction(
val id: String,
@@ -42,92 +28,29 @@ data class Restriction(
)
@Composable
fun UserRestriction(navCtrl: NavHostController) {
val localNavCtrl = rememberNavController()
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
val scrollState = rememberScrollState()
/*val titleMap = mapOf(
"Internet" to R.string.network_internet,
"Connectivity" to R.string.more_connectivity,
"Users" to R.string.users,
"Media" to R.string.media,
"Applications" to R.string.applications,
"Other" to R.string.other
)*/
Scaffold(
topBar = {
/*TopAppBar(
title = {Text(text = stringResource(titleMap[backStackEntry?.destination?.route]?:R.string.user_restrict))},
navigationIcon = {NavIcon{if(backStackEntry?.destination?.route=="Home"){navCtrl.navigateUp()}else{localNavCtrl.navigateUp()}}},
colors = TopAppBarDefaults.topAppBarColors(containerColor = colorScheme.surfaceVariant)
)*/
TopBar(backStackEntry,navCtrl,localNavCtrl){
if(backStackEntry?.destination?.route=="Home"&&scrollState.maxValue>80){
Text(
text = stringResource(R.string.user_restrict),
modifier = Modifier.alpha((maxOf(scrollState.value-30, 0)).toFloat() / 80)
)
}
}
}
) {
NavHost(
navController = localNavCtrl, startDestination = "Home",
enterTransition = Animations.navHostEnterTransition,
exitTransition = Animations.navHostExitTransition,
popEnterTransition = Animations.navHostPopEnterTransition,
popExitTransition = Animations.navHostPopExitTransition,
modifier = Modifier.padding(top = it.calculateTopPadding())
) {
composable(route = "Home") { Home(localNavCtrl, scrollState) }
composable(route = "Internet") { UserRestrictionPage(RestrictionData.internet()) }
composable(route = "Connectivity") { UserRestrictionPage(RestrictionData.connectivity())}
composable(route = "Applications") { UserRestrictionPage(RestrictionData.connectivity()) }
composable(route = "Users") { UserRestrictionPage(RestrictionData.user()) }
composable(route = "Media") { UserRestrictionPage(RestrictionData.media()) }
composable(route = "Other") { UserRestrictionPage(RestrictionData.other()) }
}
}
}
@Composable
private fun Home(navCtrl:NavHostController, scrollState: ScrollState) {
fun UserRestriction(navCtrl:NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) {
Text(
text = stringResource(R.string.user_restrict),
style = typography.headlineLarge,
modifier = Modifier.padding(top = 8.dp, bottom = 7.dp, start = 16.dp)
)
MyScaffold(R.string.user_restriction, 0.dp, navCtrl) {
Text(text = stringResource(R.string.switch_to_disable_feature), modifier = Modifier.padding(start = 16.dp))
if(context.isProfileOwner) { Text(text = stringResource(R.string.profile_owner_is_restricted), modifier = Modifier.padding(start = 16.dp)) }
if(context.isProfileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)))) {
Text(text = stringResource(R.string.some_features_invalid_in_work_profile), modifier = Modifier.padding(start = 16.dp))
}
Spacer(Modifier.padding(vertical = 2.dp))
SubPageItem(R.string.network_internet, "", R.drawable.wifi_fill0) { navCtrl.navigate("Internet") }
SubPageItem(R.string.more_connectivity, "", R.drawable.devices_other_fill0) { navCtrl.navigate("Connectivity") }
SubPageItem(R.string.applications, "", R.drawable.apps_fill0) { navCtrl.navigate("Applications") }
SubPageItem(R.string.users, "", R.drawable.account_circle_fill0) { navCtrl.navigate("Users") }
SubPageItem(R.string.media, "", R.drawable.volume_up_fill0) { navCtrl.navigate("Media") }
SubPageItem(R.string.other, "", R.drawable.more_horiz_fill0) { navCtrl.navigate("Other") }
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@Composable
private fun UserRestrictionPage(restrictions: List<Restriction>) {
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
restrictions.forEach { UserRestrictionItem(it) }
Spacer(Modifier.padding(vertical = 30.dp))
FunctionItem(R.string.network_and_internet, "", R.drawable.wifi_fill0) { navCtrl.navigate("UR-Internet") }
FunctionItem(R.string.connectivity, "", R.drawable.devices_other_fill0) { navCtrl.navigate("UR-Connectivity") }
FunctionItem(R.string.applications, "", R.drawable.apps_fill0) { navCtrl.navigate("UR-Applications") }
FunctionItem(R.string.users, "", R.drawable.account_circle_fill0) { navCtrl.navigate("UR-Users") }
FunctionItem(R.string.media, "", R.drawable.volume_up_fill0) { navCtrl.navigate("UR-Media") }
FunctionItem(R.string.other, "", R.drawable.more_horiz_fill0) { navCtrl.navigate("UR-Other") }
}
}
@SuppressLint("NewApi")
@Composable
private fun UserRestrictionItem(restriction: Restriction) {
fun UserRestrictionItem(restriction: Restriction) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -153,7 +76,7 @@ private fun UserRestrictionItem(restriction: Restriction) {
}
object RestrictionData {
fun internet(): List<Restriction> {
val internet: List<Restriction> get() {
val list = mutableListOf<Restriction>()
list += Restriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, R.string.config_mobile_network, R.drawable.signal_cellular_alt_fill0)
list += Restriction(UserManager.DISALLOW_CONFIG_WIFI, R.string.config_wifi, R.drawable.wifi_fill0)
@@ -179,7 +102,7 @@ object RestrictionData {
list += Restriction(UserManager.DISALLOW_OUTGOING_CALLS, R.string.outgoing_calls, R.drawable.phone_forwarded_fill0)
return list
}
fun connectivity(): List<Restriction> {
val connectivity: List<Restriction> get() {
val list = mutableListOf<Restriction>()
if(VERSION.SDK_INT>=26) {
list += Restriction(UserManager.DISALLOW_BLUETOOTH, R.string.bluetooth, R.drawable.bluetooth_fill0)
@@ -193,7 +116,7 @@ object RestrictionData {
if(VERSION.SDK_INT>=28) list += Restriction(UserManager.DISALLOW_PRINTING, R.string.printing, R.drawable.print_fill0)
return list
}
fun application(): List<Restriction> {
val applications: List<Restriction> get() {
val list = mutableListOf<Restriction>()
list += Restriction(UserManager.DISALLOW_INSTALL_APPS, R.string.install_app, R.drawable.android_fill0)
if(VERSION.SDK_INT>=29) list += Restriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, R.string.install_unknown_src_globally, R.drawable.android_fill0)
@@ -203,7 +126,7 @@ object RestrictionData {
if(VERSION.SDK_INT>=34) list += Restriction(UserManager.DISALLOW_CONFIG_DEFAULT_APPS, R.string.config_default_apps, R.drawable.apps_fill0)
return list
}
fun media(): List<Restriction> {
val media: List<Restriction> get() {
val list = mutableListOf<Restriction>()
if(VERSION.SDK_INT>=28) {
list += Restriction(UserManager.DISALLOW_CONFIG_BRIGHTNESS, R.string.config_brightness, R.drawable.brightness_5_fill0)
@@ -218,7 +141,7 @@ object RestrictionData {
}
return list
}
fun user(): List<Restriction> {
val users: List<Restriction> get() {
val list = mutableListOf<Restriction>()
list += Restriction(UserManager.DISALLOW_ADD_USER, R.string.add_user, R.drawable.account_circle_fill0)
list += Restriction(UserManager.DISALLOW_REMOVE_USER, R.string.remove_user, R.drawable.account_circle_fill0)
@@ -231,7 +154,7 @@ object RestrictionData {
}
return list
}
fun other(): List<Restriction> {
val other: List<Restriction> get() {
val list = mutableListOf<Restriction>()
if(VERSION.SDK_INT>=26) list += Restriction(UserManager.DISALLOW_AUTOFILL, R.string.autofill, R.drawable.password_fill0)
list += Restriction(UserManager.DISALLOW_CONFIG_CREDENTIALS, R.string.config_credentials, R.drawable.android_fill0)
@@ -254,14 +177,7 @@ object RestrictionData {
list += Restriction(UserManager.DISALLOW_DEBUGGING_FEATURES, R.string.debug_features, R.drawable.adb_fill0)
return list
}
fun getAllRestrictions(): List<String> {
val result = mutableListOf<String>()
internet().forEach { result.add(it.id) }
connectivity().forEach { result.add(it.id) }
media().forEach { result.add(it.id) }
application().forEach { result.add(it.id) }
user().forEach { result.add(it.id) }
other().forEach { result.add(it.id) }
return result
}
fun getAllRestrictions(): List<Restriction> =
internet + connectivity + media + applications + users + other
}

View File

@@ -18,20 +18,16 @@ import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.Image
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.AlertDialog
@@ -39,9 +35,7 @@ import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@@ -55,7 +49,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
@@ -65,106 +58,56 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.fileUriFlow
import com.bintianqi.owndroid.getFile
import com.bintianqi.owndroid.parseTimestamp
import com.bintianqi.owndroid.toggle
import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.CardItem
import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard
import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.SubPageItem
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.TopBar
import com.bintianqi.owndroid.uriToStream
import com.bintianqi.owndroid.yesOrNo
@Composable
fun UserManage(navCtrl: NavHostController) {
val localNavCtrl = rememberNavController()
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
val scrollState = rememberScrollState()
Scaffold(
topBar = {
TopBar(backStackEntry, navCtrl, localNavCtrl) {
if(backStackEntry?.destination?.route == "Home" && scrollState.maxValue > 100) {
Text(
text = stringResource(R.string.users),
modifier = Modifier.alpha((maxOf(scrollState.value-30, 0)).toFloat() / 80)
)
}
}
}
) {
NavHost(
navController = localNavCtrl, startDestination = "Home",
enterTransition = Animations.navHostEnterTransition,
exitTransition = Animations.navHostExitTransition,
popEnterTransition = Animations.navHostPopEnterTransition,
popExitTransition = Animations.navHostPopExitTransition,
modifier = Modifier.padding(top = it.calculateTopPadding())
) {
composable(route = "Home") { Home(localNavCtrl, scrollState) }
composable(route = "UserInfo") { CurrentUserInfo() }
composable(route = "Options") { Options() }
composable(route = "UserOperation") { UserOperation() }
composable(route = "CreateUser") { CreateUser() }
composable(route = "EditUsername") { Username() }
composable(route = "ChangeUserIcon") { UserIcon() }
composable(route = "UserSessionMessage") { UserSessionMessage() }
composable(route = "AffiliationID") { AffiliationID() }
}
}
}
@Composable
private fun Home(navCtrl: NavHostController,scrollState: ScrollState) {
fun Users(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
//var logoutDialog by remember { mutableStateOf(false) }
var dialog by remember { mutableIntStateOf(0) }
Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) {
Text(
text = stringResource(R.string.users),
style = typography.headlineLarge,
modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 16.dp)
)
SubPageItem(R.string.user_info, "", R.drawable.person_fill0) { navCtrl.navigate("UserInfo") }
MyScaffold(R.string.users, 0.dp, navCtrl) {
FunctionItem(R.string.user_info, "", R.drawable.person_fill0) { navCtrl.navigate("UserInfo") }
if(deviceOwner && VERSION.SDK_INT >= 28) {
SubPageItem(R.string.secondary_users, "", R.drawable.list_fill0) { dialog = 1 }
SubPageItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Options") }
FunctionItem(R.string.secondary_users, "", R.drawable.list_fill0) { dialog = 1 }
FunctionItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("UserOptions") }
}
if(deviceOwner) {
SubPageItem(R.string.user_operation, "", R.drawable.sync_alt_fill0) { navCtrl.navigate("UserOperation") }
FunctionItem(R.string.user_operation, "", R.drawable.sync_alt_fill0) { navCtrl.navigate("UserOperation") }
}
if(VERSION.SDK_INT >= 24 && deviceOwner) {
SubPageItem(R.string.create_user, "", R.drawable.person_add_fill0) { navCtrl.navigate("CreateUser") }
FunctionItem(R.string.create_user, "", R.drawable.person_add_fill0) { navCtrl.navigate("CreateUser") }
}
if(VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser) {
SubPageItem(R.string.logout_current_user, "", R.drawable.logout_fill0) { dialog = 2 }
FunctionItem(R.string.logout_current_user, "", R.drawable.logout_fill0) { dialog = 2 }
}
if(deviceOwner || profileOwner) {
SubPageItem(R.string.change_username, "", R.drawable.edit_fill0) { navCtrl.navigate("EditUsername") }
FunctionItem(R.string.change_username, "", R.drawable.edit_fill0) { navCtrl.navigate("ChangeUsername") }
}
if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.change_user_icon, "", R.drawable.account_circle_fill0) { navCtrl.navigate("ChangeUserIcon") }
FunctionItem(R.string.change_user_icon, "", R.drawable.account_circle_fill0) { navCtrl.navigate("ChangeUserIcon") }
}
if(VERSION.SDK_INT >= 28 && deviceOwner) {
SubPageItem(R.string.user_session_msg, "", R.drawable.notifications_fill0) { navCtrl.navigate("UserSessionMessage") }
FunctionItem(R.string.user_session_msg, "", R.drawable.notifications_fill0) { navCtrl.navigate("UserSessionMessage") }
}
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.affiliation_id, "", R.drawable.id_card_fill0) { navCtrl.navigate("AffiliationID") }
FunctionItem(R.string.affiliation_id, "", R.drawable.id_card_fill0) { navCtrl.navigate("AffiliationID") }
}
Spacer(Modifier.padding(vertical = 30.dp))
LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") }
}
if(dialog != 0 && VERSION.SDK_INT >= 28) AlertDialog(
@@ -208,11 +151,11 @@ private fun Home(navCtrl: NavHostController,scrollState: ScrollState) {
}
@Composable
private fun Options() {
fun UserOptions(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
MyScaffold(R.string.options, 0.dp, navCtrl) {
if(VERSION.SDK_INT >= 28) {
SwitchItem(R.string.enable_logout, "", null, { dpm.isLogoutEnabled }, { dpm.setLogoutEnabled(receiver, it) })
}
@@ -220,17 +163,14 @@ private fun Options() {
}
@Composable
private fun CurrentUserInfo() {
fun CurrentUserInfo(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
val user = Process.myUserHandle()
var infoDialog by remember { mutableIntStateOf(0) }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.user_info), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.user_info, 8.dp, navCtrl) {
if(VERSION.SDK_INT >= 24) CardItem(R.string.support_multiuser, UserManager.supportsMultipleUsers().yesOrNo)
if(VERSION.SDK_INT >= 31) CardItem(R.string.headless_system_user_mode, UserManager.isHeadlessSystemUserMode().yesOrNo) { infoDialog = 1 }
Spacer(Modifier.padding(vertical = 8.dp))
@@ -247,7 +187,6 @@ private fun CurrentUserInfo() {
}
CardItem(R.string.user_id, (Binder.getCallingUid() / 100000).toString())
CardItem(R.string.user_serial_number, userManager.getSerialNumberForUser(Process.myUserHandle()).toString())
Spacer(Modifier.padding(vertical = 30.dp))
}
if(infoDialog != 0) AlertDialog(
text = { Text(stringResource(R.string.info_headless_system_user_mode)) },
@@ -261,7 +200,7 @@ private fun CurrentUserInfo() {
}
@Composable
private fun UserOperation() {
fun UserOperation(navCtrl: NavHostController) {
val context = LocalContext.current
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
val dpm = context.getDPM()
@@ -287,10 +226,7 @@ private fun UserOperation() {
} catch(_: Exception) {
false
}
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.user_operation), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.user_operation, 8.dp, navCtrl) {
OutlinedTextField(
value = idInput,
onValueChange = {
@@ -365,13 +301,12 @@ private fun UserOperation() {
Text(stringResource(R.string.delete))
}
InfoCard(R.string.info_user_operation)
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@SuppressLint("NewApi")
@Composable
private fun CreateUser() {
fun CreateUser(navCtrl: NavHostController) {
val context = LocalContext.current
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
val dpm = context.getDPM()
@@ -379,10 +314,7 @@ private fun CreateUser() {
val focusMgr = LocalFocusManager.current
var userName by remember { mutableStateOf("") }
val flags = remember { mutableStateListOf<Int>() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.create_user), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.create_user, 8.dp, navCtrl) {
OutlinedTextField(
value = userName,
onValueChange = { userName= it },
@@ -423,13 +355,12 @@ private fun CreateUser() {
}
Spacer(Modifier.padding(vertical = 5.dp))
if(newUserHandle != null) { Text(text = stringResource(R.string.serial_number_of_new_user_is, userManager.getSerialNumberForUser(newUserHandle))) }
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@SuppressLint("NewApi")
@Composable
private fun AffiliationID() {
fun AffiliationID(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -441,10 +372,7 @@ private fun AffiliationID() {
list.addAll(dpm.getAffiliationIds(receiver))
}
LaunchedEffect(Unit) { refreshIds() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.affiliation_id), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.affiliation_id, 8.dp, navCtrl) {
Column(modifier = Modifier.animateContentSize()) {
if(list.isEmpty()) Text(stringResource(R.string.none))
for(i in list) {
@@ -483,21 +411,17 @@ private fun AffiliationID() {
Text(stringResource(R.string.apply))
}
InfoCard(R.string.info_affiliated_id)
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@Composable
private fun Username() {
fun ChangeUsername(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
var inputUsername by remember { mutableStateOf("") }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.change_username), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.change_username, 8.dp, navCtrl) {
OutlinedTextField(
value = inputUsername,
onValueChange = { inputUsername= it },
@@ -527,7 +451,7 @@ private fun Username() {
@SuppressLint("NewApi")
@Composable
private fun UserSessionMessage() {
fun UserSessionMessage(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -539,10 +463,7 @@ private fun UserSessionMessage() {
end = dpm.getEndUserSessionMessage(receiver)?.toString() ?: ""
}
LaunchedEffect(Unit) { refreshMsg() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.user_session_msg), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.user_session_msg, 8.dp, navCtrl) {
OutlinedTextField(
value = start,
onValueChange = { start= it },
@@ -604,13 +525,12 @@ private fun UserSessionMessage() {
Text(stringResource(R.string.reset))
}
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@SuppressLint("NewApi")
@Composable
private fun UserIcon() {
fun ChangeUserIcon(navCtrl: NavHostController) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -623,10 +543,7 @@ private fun UserIcon() {
bitmap = BitmapFactory.decodeStream(stream)
}
}
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.change_user_icon), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
MyScaffold(R.string.change_user_icon, 8.dp, navCtrl) {
CheckBoxItem(R.string.file_picker_instead_gallery, getContent, { getContent = it })
Spacer(Modifier.padding(vertical = 5.dp))
Button(

View File

@@ -8,8 +8,10 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material3.*
@@ -18,11 +20,13 @@ import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavHostController
@@ -33,9 +37,9 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Composable
fun SubPageItem(
fun FunctionItem(
@StringRes title: Int,
desc:String,
desc: String,
@DrawableRes icon: Int? = null,
operation: () -> Unit
) {
@@ -48,8 +52,7 @@ fun SubPageItem(
verticalAlignment = Alignment.CenterVertically
) {
if(icon != null) Icon(
painter = painterResource(icon),
contentDescription = null,
painter = painterResource(icon), contentDescription = null,
modifier = Modifier.padding(top = 1.dp, end = 20.dp).offset(x = (-2).dp)
)
Column {
@@ -194,25 +197,6 @@ fun SwitchItem(
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TopBar(
backStackEntry: NavBackStackEntry?,
navCtrl: NavHostController,
localNavCtrl: NavHostController,
title: @Composable ()->Unit = {}
) {
TopAppBar(
title = title,
navigationIcon = {
NavIcon{
if(backStackEntry?.destination?.route == "Home") { navCtrl.navigateUp() } else { localNavCtrl.navigateUp() }
}
},
colors = TopAppBarDefaults.topAppBarColors(containerColor = colorScheme.background)
)
}
@Composable
fun CopyTextButton(@StringRes label: Int, content: String) {
val context = LocalContext.current
@@ -287,6 +271,7 @@ fun ListItem(text: String, onDelete: () -> Unit) {
fun InfoCard(@StringRes strID: Int) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
.clip(RoundedCornerShape(10))
.background(color = colorScheme.tertiaryContainer)
@@ -296,3 +281,53 @@ fun InfoCard(@StringRes strID: Int) {
Text(stringResource(strID))
}
}
@Composable
fun MyScaffold(
@StringRes title: Int,
horizonPadding: Dp,
navCtrl: NavHostController,
displayTitle: Boolean = true,
content: @Composable ColumnScope.() -> Unit
) = MyScaffold(title, horizonPadding, { navCtrl.navigateUp() }, displayTitle, content)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyScaffold(
@StringRes title: Int,
horizonPadding: Dp,
onNavIconClicked: () -> Unit,
displayTitle: Boolean,
content: @Composable ColumnScope.() -> Unit
) {
val scrollState = rememberScrollState()
Scaffold(
topBar = {
TopAppBar(
title = {
Text(
text = stringResource(title),
modifier = if(displayTitle) Modifier.alpha((maxOf(scrollState.value-90,0)).toFloat()/50) else Modifier
)
},
navigationIcon = { NavIcon (onNavIconClicked) }
)
}
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.padding(horizontal = horizonPadding)
.verticalScroll(scrollState)
.padding(bottom = 80.dp)
) {
if(displayTitle) Text(
text = stringResource(title),
style = typography.headlineLarge,
modifier = Modifier.padding(start = if(horizonPadding == 0.dp) 16.dp else 0.dp,top = 10.dp, bottom = 5.dp)
)
content()
}
}
}

View File

@@ -91,8 +91,8 @@
<string name="account_type">Тип аккаунта</string>
<string name="transfer_ownership">Передача прав владения</string>
<string name="target_component_name">Имя целевого компонента</string>
<string name="device_owner_lock_screen_info">Информация на экране блокировки</string>
<string name="support_msg">Сообщение поддержки</string>
<string name="lock_screen_info">Информация на экране блокировки</string>
<string name="support_messages">Сообщение поддержки</string>
<string name="short_support_msg">Краткое сообщение</string>
<string name="long_support_msg">Подробное сообщение</string>
<string name="transfer">Передать</string>
@@ -175,7 +175,7 @@
<string name="cert_installed">Сертификат установлен: %1$s</string>
<string name="select_ca_cert" tools:ignore="TypographyEllipsis">Выберите сертификат...</string>
<string name="uninstall_all_user_ca_cert">Удалить все пользовательские CA-сертификаты</string>
<string name="security_logs">Журналы безопасности</string>
<string name="security_logging">Security logging</string> <!--TODO-->
<string name="pre_reboot_security_logs">Журналы безопасности перед перезагрузкой</string>
<string name="wipe_data">Стереть данные</string>
<string name="wipe_external_storage">Стереть внешнее хранилище</string>
@@ -186,7 +186,6 @@
<string name="wipe_work_profile_warning">Ваш рабочий профиль будет УДАЛЕН</string>
<string name="encryption_status">Статус шифрования</string>
<string name="frp_policy">Политика FRP</string>
<string name="factory_reset_protection_policy">Политика защиты от сброса к заводским настройкам</string>
<string name="frp_policy_not_supported">Политика FRP не поддерживается на этом устройстве</string>
<string name="enable_frp">Включить FRP</string>
<string name="account_list_is">Список аккаунтов: </string>
@@ -236,12 +235,11 @@
<string name="specify_port">Указать порт</string>
<string name="invalid_config">Неверная конфигурация</string>
<string name="exclude_hosts">Исключить хосты</string>
<string name="retrieve_net_logs">Журналы сети</string>
<string name="network_logging">Network logging</string> <!--TODO-->
<string name="log_file_size_is">Размер файла журнала: %1$s</string>
<string name="delete_logs">Удалить журналы</string>
<string name="export_logs">Экспортировать журналы</string>
<string name="wifi_auth_keypair">Пара ключей Wi-Fi</string>
<string name="keypair">Пара ключей</string>
<string name="preferential_network_service">Предпочтительная сетевая служба</string>
<string name="network_id">Идентификатор сети</string>
<string name="allow_fallback_to_default_connection">Разрешить переход на соединение по умолчанию</string>
@@ -286,7 +284,6 @@
<string name="migrate_account">Перенести аккаунт</string>
<string name="account_name">Имя аккаунта</string>
<string name="keep_account">Сохранить аккаунт</string>
<string name="is_org_owned_profile">Рабочий профиль принадлежит организации: %1$s</string>
<string name="org_owned_work_profile">Рабочий профиль организации</string>
<string name="skip_encryption">Пропустить шифрование</string>
<string name="create">Создать</string>
@@ -316,7 +313,7 @@
<string name="always_on_vpn">Постоянный VPN</string>
<string name="enable_lockdown">Включить блокировку</string>
<string name="clear_current_config">Очистить текущую конфигурацию</string>
<string name="permission">Разрешение</string>
<string name="permissions">Разрешение</string>
<string name="scope_is_work_profile">Область действия: рабочий профиль</string>
<string name="app_info">Информация о приложении</string>
<string name="not_installed">Не установлено</string>
@@ -366,12 +363,12 @@
<!--Ограничения пользователя-->
<string name="user_restrict">Ограничения пользователя</string>
<string name="user_restriction">Ограничения пользователя</string>
<string name="profile_owner_is_restricted">Владелец профиля может использовать ограниченные функции</string>
<string name="switch_to_disable_feature">Включите переключатель, чтобы отключить эту функцию.</string>
<string name="some_features_invalid_in_work_profile">Функции в рабочем профиле ограничены.</string>
<string name="network_internet">Сеть</string>
<string name="more_connectivity">Другие подключения</string>
<string name="network_and_internet">Сеть</string>
<string name="connectivity">Другие подключения</string>
<string name="media">Медиа</string>
<string name="other">Другое</string>
<string name="require_device_owner">Требуется владелец устройства</string>
@@ -478,7 +475,6 @@
<string name="change_user_icon">Изменить значок пользователя</string>
<string name="file_picker_instead_gallery">Использовать выборщик файлов вместо галереи</string>
<string name="select_picture" tools:ignore="TypographyEllipsis">Выберите изображение...</string>
<string name="unknown_result">Неизвестный результат (возможно, ошибка)</string>
<string name="fail_managed_profile">Ошибка: управляемый профиль</string>
<string name="fail_current_user">Ошибка: текущий пользователь</string>
<string name="user_session_msg">Сообщение о сеансе пользователя</string>

View File

@@ -92,8 +92,8 @@
<string name="account_type">Hesap Türleri</string>
<string name="transfer_ownership">Sahipliği Devret</string>
<string name="target_component_name">Target component name</string> <!--TODO-->
<string name="device_owner_lock_screen_info">Ekran Kilidi Bilgisi</string>
<string name="support_msg">Destek Mesajı</string>
<string name="lock_screen_info">Ekran Kilidi Bilgisi</string>
<string name="support_messages">Destek Mesajı</string>
<string name="short_support_msg">Kısa Mesaj</string>
<string name="long_support_msg">Uzun Mesaj</string>
<string name="transfer">Transfer</string>
@@ -177,7 +177,7 @@
<string name="cert_installed">Yüklenen sertifika: %1$s</string> <!--TODO-->
<string name="select_ca_cert" tools:ignore="TypographyEllipsis">Sertifika seç...</string> <!--TODO-->
<string name="uninstall_all_user_ca_cert">Tüm kullanıcı sertifikalarını kaldır</string> <!--TODO-->
<string name="security_logs">Güvenlik kayıtları</string>
<string name="security_logging">Security logging</string> <!--TODO-->
<string name="pre_reboot_security_logs">Yeniden başlatmadan önce güvenlik kayıtları</string>
<string name="wipe_data">Verileri sil</string>
<string name="wipe_external_storage">Harici depolamayı sil</string>
@@ -188,7 +188,6 @@
<string name="wipe_work_profile_warning">Your work profile will be DELETED</string> <!--TODO-->
<string name="encryption_status">Encryption status</string> <!--TODO-->
<string name="frp_policy">FRP politikası</string>
<string name="factory_reset_protection_policy">Fabrika ayarlarına sıfırlama koruma politikası</string>
<string name="frp_policy_not_supported">FRP politikası bu cihazda desteklenmiyor</string>
<string name="enable_frp">FRP\'yi etkinleştir</string>
<string name="account_list_is">"Hesap listesi: "</string>
@@ -237,12 +236,11 @@
<string name="specify_port">Specify port</string> <!--TODO-->
<string name="invalid_config">Invalid config</string> <!--TODO-->
<string name="exclude_hosts">Exclude hosts</string> <!--TODO-->
<string name="retrieve_net_logs">Ağ kayıtları</string>
<string name="network_logging">Network logging</string> <!--TODO-->
<string name="log_file_size_is">Log file size: %1$s</string> <!--TODO-->
<string name="delete_logs">Delete logs</string> <!--TODO-->
<string name="export_logs">Export logs</string> <!--TODO-->
<string name="wifi_auth_keypair">Wi-Fi anahtar çifti</string>
<string name="keypair">Anahtar çifti</string>
<string name="preferential_network_service">Tercihli ağ hizmeti</string>
<string name="network_id">Network ID</string> <!--TODO-->
<string name="allow_fallback_to_default_connection">Allow fallback to default connection</string> <!--TODO-->
@@ -285,7 +283,6 @@
<string name="migrate_account">Migrate account</string> <!--TODO-->
<string name="account_name">Account name</string> <!--TODO-->
<string name="keep_account">Keep account</string> <!--TODO-->
<string name="is_org_owned_profile">Kuruluşa ait iş profili: %1$s</string>
<string name="org_owned_work_profile">Kuruluş iş profili</string>
<string name="skip_encryption">Şifrelemeyi atla</string>
<string name="create">Oluştur</string>
@@ -314,7 +311,7 @@
<string name="always_on_vpn">Her zaman açık VPN</string>
<string name="enable_lockdown">Enable lockdown</string> <!--TODO-->
<string name="clear_current_config">Clear current config</string> <!--TODO-->
<string name="permission">İzin</string>
<string name="permissions">İzin</string>
<string name="scope_is_work_profile">Kapsam: iş profili</string>
<string name="app_info">Uygulama bilgisi</string>
<string name="not_installed">Yüklü değil</string>
@@ -365,12 +362,12 @@
<string name="status_fail_timeout">Başarısız: zaman aşımı</string>
<!--UserRestriction-->
<string name="user_restrict">Kullanıcı kısıtlaması</string>
<string name="user_restriction">Kullanıcı kısıtlaması</string>
<string name="profile_owner_is_restricted">Profil sahibi sınırlı işlev kullanabilir</string>
<string name="switch_to_disable_feature">Bu işlevi devre dışı bırakmak için bir anahtar açın. </string>
<string name="some_features_invalid_in_work_profile">İş profilindeki işlevler sınırlıdır. </string>
<string name="network_internet"></string>
<string name="more_connectivity">Diğer bağlantı</string>
<string name="network_and_internet"></string>
<string name="connectivity">Diğer bağlantı</string>
<string name="media">Medya</string>
<string name="other">Diğer</string>
<string name="require_device_owner">Cihaz sahibi gerektirir</string>
@@ -476,7 +473,6 @@
<string name="change_user_icon">Kullanıcı simgesini değiştir</string>
<string name="file_picker_instead_gallery">Galeri yerine dosya seçici kullan</string>
<string name="select_picture" tools:ignore="TypographyEllipsis">Resim seç...</string>
<string name="unknown_result">Bilinmeyen sonuç (başarısız olabilir)</string>
<string name="fail_managed_profile">Başarısız: yönetilen profil</string>
<string name="fail_current_user">Başarısız: mevcut kullanıcı</string>
<string name="user_session_msg">Kullanıcı oturum mesajı</string>

View File

@@ -87,8 +87,8 @@
<string name="account_type">账号类型</string>
<string name="transfer_ownership">转移所有权</string>
<string name="target_component_name">目标组件名</string>
<string name="device_owner_lock_screen_info">锁屏提示信息</string>
<string name="support_msg">提供支持的消息</string>
<string name="lock_screen_info">锁屏提示信息</string>
<string name="support_messages">提供支持的消息</string>
<string name="short_support_msg">提供支持的短消息</string>
<string name="long_support_msg">提供支持的长消息</string>
<string name="transfer">转移</string>
@@ -172,7 +172,7 @@
<string name="cert_installed">证书已安装:%1$s</string>
<string name="select_ca_cert" tools:ignore="TypographyEllipsis">选择证书...</string>
<string name="uninstall_all_user_ca_cert">卸载所有用户证书</string>
<string name="security_logs">安全日志</string>
<string name="security_logging">安全日志</string>
<string name="pre_reboot_security_logs">重启前安全日志</string>
<string name="wipe_data">清除数据</string>
<string name="wipe_external_storage">清除外部存储</string>
@@ -183,7 +183,6 @@
<string name="wipe_work_profile_warning">你的工作资料将会被删除</string>
<string name="encryption_status">加密状态</string>
<string name="frp_policy">FRP策略</string>
<string name="factory_reset_protection_policy">恢复出厂设置保护策略</string>
<string name="frp_policy_not_supported">这个设备不支持恢复出厂设置保护策略</string>
<string name="enable_frp">启用FRP</string>
<string name="account_list_is">账户列表:</string>
@@ -232,12 +231,11 @@
<string name="specify_port">指定端口</string>
<string name="invalid_config">无效配置</string>
<string name="exclude_hosts">排除列表</string>
<string name="retrieve_net_logs">收集网络日志</string>
<string name="network_logging">网络日志</string>
<string name="log_file_size_is">日志文件大小:%1$s</string>
<string name="delete_logs">删除日志</string>
<string name="export_logs">导出日志</string>
<string name="wifi_auth_keypair">Wi-Fi密钥对</string>
<string name="keypair">密钥对</string>
<string name="preferential_network_service">首选网络服务</string>
<string name="network_id">网络ID</string>
<string name="allow_fallback_to_default_connection">允许回落到默认连接</string>
@@ -280,7 +278,6 @@
<string name="migrate_account">迁移账号</string>
<string name="account_name">账号名</string>
<string name="keep_account">保留账号</string>
<string name="is_org_owned_profile">由组织拥有的工作资料:%1$s</string>
<string name="org_owned_work_profile">组织拥有的工作资料</string>
<string name="skip_encryption">跳过加密</string>
<string name="create">创建</string>
@@ -309,7 +306,7 @@
<string name="always_on_vpn">VPN保持打开</string>
<string name="enable_lockdown">启用锁定</string>
<string name="clear_current_config">清除当前配置</string>
<string name="permission">权限</string>
<string name="permissions">权限</string>
<string name="scope_is_work_profile">作用域: 工作资料</string>
<string name="app_info">应用详情</string>
<string name="not_installed">未安装</string>
@@ -357,12 +354,12 @@
<string name="status_fail_timeout">超时</string>
<!--UserRestriction-->
<string name="user_restrict">用户限制</string>
<string name="user_restriction">用户限制</string>
<string name="profile_owner_is_restricted">Profile owner无法使用部分功能</string>
<string name="switch_to_disable_feature">打开开关后会禁用对应的功能</string>
<string name="some_features_invalid_in_work_profile">工作资料中部分功能无效</string>
<string name="network_internet">网络和互联网</string>
<string name="more_connectivity">更多连接</string>
<string name="network_and_internet">网络和互联网</string>
<string name="connectivity">更多连接</string>
<string name="media">媒体</string>
<string name="other">其他</string>
<string name="require_device_owner">需要DeviceOwner</string>
@@ -468,7 +465,6 @@
<string name="change_user_icon">更换用户头像</string>
<string name="file_picker_instead_gallery">使用文件选择器而不是相册</string>
<string name="select_picture" tools:ignore="TypographyEllipsis">选择图片...</string>
<string name="unknown_result">未知结果(失败)</string>
<string name="fail_managed_profile">失败:受管理的资料</string>
<string name="fail_current_user">失败:当前用户</string>
<string name="user_session_msg">用户会话消息</string>

View File

@@ -94,8 +94,8 @@
<string name="account_type">Account type</string>
<string name="transfer_ownership">Transfer Ownership</string>
<string name="target_component_name">Target component name</string>
<string name="device_owner_lock_screen_info">Lockscreen info</string>
<string name="support_msg">Support Message</string>
<string name="lock_screen_info">Lockscreen info</string>
<string name="support_messages">Support Messages</string>
<string name="short_support_msg">Short message</string>
<string name="long_support_msg">Long message</string>
<string name="transfer">Transfer</string>
@@ -181,7 +181,7 @@
<string name="cert_installed">Certificate installed: %1$s</string>
<string name="select_ca_cert" tools:ignore="TypographyEllipsis">Select certificate...</string>
<string name="uninstall_all_user_ca_cert">Uninstall all user CA certificate</string>
<string name="security_logs">Security logs</string>
<string name="security_logging">Security logging</string>
<string name="pre_reboot_security_logs">Pre-reboot security logs</string>
<string name="wipe_data">Wipe data</string>
<string name="wipe_external_storage">Wipe external storage</string>
@@ -192,7 +192,6 @@
<string name="wipe_work_profile_warning">Your work profile will be DELETED</string>
<string name="encryption_status">Encryption status</string>
<string name="frp_policy">FRP policy</string>
<string name="factory_reset_protection_policy">Factory reset protection policy</string>
<string name="frp_policy_not_supported">FRP policy is not supported on this device</string>
<string name="enable_frp">Enable FRP</string>
<string name="account_list_is">"Account list: "</string>
@@ -242,12 +241,11 @@
<string name="specify_port">Specify port</string>
<string name="invalid_config">Invalid config</string>
<string name="exclude_hosts">Exclude hosts</string>
<string name="retrieve_net_logs">Network logs</string>
<string name="network_logging">Network logging</string>
<string name="log_file_size_is">Log file size: %1$s</string>
<string name="delete_logs">Delete logs</string>
<string name="export_logs">Export logs</string>
<string name="wifi_auth_keypair">Wi-Fi keypair</string>
<string name="keypair">Keypair</string>
<string name="preferential_network_service">Preferential network service</string>
<string name="network_id">Network ID</string>
<string name="allow_fallback_to_default_connection">Allow fallback to default connection</string>
@@ -290,7 +288,6 @@
<string name="migrate_account">Migrate account</string>
<string name="account_name">Account name</string>
<string name="keep_account">Keep account</string>
<string name="is_org_owned_profile">Organization owned work profile: %1$s</string>
<string name="org_owned_work_profile">Organization work profile</string>
<string name="activate_org_profile_command" tools:ignore="TypographyDashes" translatable="false">
dpm mark-profile-owner-on-organization-owned-device --user %1$s com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver
@@ -322,7 +319,7 @@
<string name="always_on_vpn">Always-on VPN</string>
<string name="enable_lockdown">Enable lockdown</string>
<string name="clear_current_config">Clear current config</string>
<string name="permission">Permission</string>
<string name="permissions">Permissions</string>
<string name="scope_is_work_profile">Scope: work profile</string>
<string name="app_info">App info</string>
<string name="not_installed">Not installed</string>
@@ -371,12 +368,12 @@
<string name="status_fail_timeout">Fail: timeout</string>
<!--UserRestriction-->
<string name="user_restrict">User restriction</string>
<string name="user_restriction">User restriction</string>
<string name="profile_owner_is_restricted">Profile owner can use limited function</string>
<string name="switch_to_disable_feature">Turn on a switch to disable that function. </string>
<string name="some_features_invalid_in_work_profile">Functions in work profile is limited. </string>
<string name="network_internet">Network</string>
<string name="more_connectivity">Other connection</string>
<string name="network_and_internet">Network</string>
<string name="connectivity">Other connection</string>
<string name="media">Media</string>
<string name="other">Other</string>
<string name="require_device_owner">Require device owner</string>
@@ -482,7 +479,6 @@
<string name="change_user_icon">Change user icon</string>
<string name="file_picker_instead_gallery">Use file picker instead of gallery</string>
<string name="select_picture" tools:ignore="TypographyEllipsis">Select image...</string>
<string name="unknown_result">Unknown result(may failed)</string>
<string name="fail_managed_profile">Failed: managed profile</string>
<string name="fail_current_user">Failed: current user</string>
<string name="user_session_msg">User session message</string>
@@ -631,7 +627,7 @@
<string name="info_security_log">If a Device owner use this function, all users should be affiliated.</string>
<string name="info_pre_reboot_security_log">Not all devices support pre-reboot security logs.</string>
<string name="info_disable_account_management">When account management is disabled for an account type, adding or removing an account of that type will not be possible.</string>
<string name="info_frp_policy">FRP can protect the device after untrusted factory reset (in Recovery or Fastboot).\nTo enable this feature, the device must support persistent data block service.</string>
<string name="info_frp_policy">Factory reset protection can protect the device after untrusted factory reset (in Recovery or Fastboot).\nTo enable this feature, the device must support persistent data block service.</string>
<string name="info_wipe_data_in_managed_user">All data of this user will be wiped, but that user won\'t be removed.</string>
<string name="info_lockdown_admin_configured_network">Control whether the user can change networks configured by the admin.\nWhen this lockdown is enabled, the user can still configure and connect to other Wi-Fi networks, or use other Wi-Fi capabilities such as tethering.</string>
<string name="info_minimum_wifi_security_level">Specify the minimum security level required for Wi-Fi networks. The device may not connect to networks that do not meet the minimum security level. If the current network does not meet the minimum security level set, it will be disconnected.</string>