mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-24 03:16:00 +00:00
Use main NavHost to navigate across all pages
Upgrade dependencies Upload dependencies to GitHub in workflow
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 == "" }
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user