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

@@ -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(