mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-30 05:16:01 +00:00
@@ -0,0 +1,906 @@
|
||||
package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT
|
||||
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
|
||||
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED
|
||||
import android.app.admin.PackagePolicy
|
||||
import android.app.admin.PackagePolicy.PACKAGE_POLICY_ALLOWLIST
|
||||
import android.app.admin.PackagePolicy.PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM
|
||||
import android.app.admin.PackagePolicy.PACKAGE_POLICY_BLOCKLIST
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.content.pm.PackageManager.NameNotFoundException
|
||||
import android.net.Uri
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Looper
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
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.*
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.ui.*
|
||||
import com.bintianqi.owndroid.ui.theme.bgColor
|
||||
import kotlinx.coroutines.delay
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
private var credentialList = mutableSetOf<String>()
|
||||
private var crossProfilePkg = mutableSetOf<String>()
|
||||
private var keepUninstallPkg = mutableListOf<String>()
|
||||
private var permittedIme = mutableListOf<String>()
|
||||
private var permittedAccessibility = mutableListOf<String>()
|
||||
|
||||
@Composable
|
||||
fun ApplicationManage(navCtrl:NavHostController){
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var pkgName by rememberSaveable{ mutableStateOf("") }
|
||||
val localNavCtrl = rememberNavController()
|
||||
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
|
||||
val titleMap = mapOf(
|
||||
"BlockUninstall" to R.string.block_uninstall,
|
||||
"UserControlDisabled" to R.string.ucd,
|
||||
"PermissionManage" to R.string.permission_manage,
|
||||
"CrossProfilePackage" to R.string.cross_profile_package,
|
||||
"CrossProfileWidget" to R.string.cross_profile_widget,
|
||||
"CredentialManagePolicy" to R.string.credential_manage_policy,
|
||||
"Accessibility" to R.string.permitted_accessibility_app,
|
||||
"IME" to R.string.permitted_ime,
|
||||
"KeepUninstalled" to R.string.keep_uninstalled_pkgs,
|
||||
"InstallApp" to R.string.install_app,
|
||||
"UninstallApp" to R.string.uninstall_app,
|
||||
"ClearAppData" to R.string.clear_app_data,
|
||||
"DefaultDialer" to R.string.set_default_dialer,
|
||||
)
|
||||
Scaffold(
|
||||
topBar = {
|
||||
/*TopAppBar(
|
||||
title = {Text(text = stringResource(titleMap[backStackEntry?.destination?.route]?:R.string.app_manage))},
|
||||
navigationIcon = {NavIcon{if(backStackEntry?.destination?.route=="Home"){navCtrl.navigateUp()}else{localNavCtrl.navigateUp()}}},
|
||||
colors = TopAppBarDefaults.topAppBarColors(containerColor = colorScheme.surfaceVariant)
|
||||
)*/
|
||||
TopBar(backStackEntry, navCtrl, localNavCtrl){Text(text = stringResource(titleMap[backStackEntry?.destination?.route] ?: R.string.app_manager))}
|
||||
}
|
||||
){ paddingValues->
|
||||
Column(modifier = Modifier.fillMaxSize().padding(top = paddingValues.calculateTopPadding())){
|
||||
LaunchedEffect(Unit) {
|
||||
while(true){
|
||||
if(applySelectedPackage){ pkgName = selectedPackage; applySelectedPackage = false; applySelectedPermission = true}
|
||||
delay(100)
|
||||
}
|
||||
}
|
||||
if(backStackEntry?.destination?.route!="InstallApp"){
|
||||
TextField(
|
||||
value = pkgName,
|
||||
onValueChange = { pkgName = it },
|
||||
label = { Text(stringResource(R.string.package_name)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
trailingIcon = {
|
||||
Icon(painter = painterResource(R.drawable.checklist_fill0), contentDescription = null,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(50))
|
||||
.clickable(onClick = {navCtrl.navigate("PackageSelector")})
|
||||
.padding(3.dp))
|
||||
},
|
||||
singleLine = true
|
||||
)
|
||||
}
|
||||
NavHost(
|
||||
navController = localNavCtrl, startDestination = "Home",
|
||||
enterTransition = Animations().navHostEnterTransition,
|
||||
exitTransition = Animations().navHostExitTransition,
|
||||
popEnterTransition = Animations().navHostPopEnterTransition,
|
||||
popExitTransition = Animations().navHostPopExitTransition,
|
||||
modifier = Modifier.background(bgColor)
|
||||
){
|
||||
composable(route = "Home"){Home(localNavCtrl,pkgName)}
|
||||
composable(route = "BlockUninstall"){BlockUninstall(pkgName)}
|
||||
composable(route = "UserControlDisabled"){UserCtrlDisabledPkg(pkgName)}
|
||||
composable(route = "PermissionManage"){PermissionManage(pkgName,navCtrl)}
|
||||
composable(route = "CrossProfilePackage"){CrossProfilePkg(pkgName)}
|
||||
composable(route = "CrossProfileWidget"){CrossProfileWidget(pkgName)}
|
||||
composable(route = "CredentialManagePolicy"){CredentialManagePolicy(pkgName)}
|
||||
composable(route = "Accessibility"){PermittedAccessibility(pkgName)}
|
||||
composable(route = "IME"){PermittedIME(pkgName)}
|
||||
composable(route = "KeepUninstalled"){KeepUninstalledApp(pkgName)}
|
||||
composable(route = "InstallApp"){InstallApp()}
|
||||
composable(route = "UninstallApp"){UninstallApp(pkgName)}
|
||||
composable(route = "ClearAppData"){ClearAppData(pkgName)}
|
||||
composable(route = "DefaultDialer"){DefaultDialerApp(pkgName)}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Home(navCtrl:NavHostController, pkgName: String){
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext, Receiver::class.java)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(VERSION.SDK_INT>=24&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
|
||||
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){
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
intent.setData(Uri.parse("package:$pkgName"))
|
||||
startActivity(myContext,intent,null)
|
||||
}
|
||||
if(VERSION.SDK_INT>=24&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
|
||||
SwitchItem(
|
||||
R.string.suspend,"",R.drawable.block_fill0,
|
||||
{
|
||||
try{ myDpm.isPackageSuspended(myComponent,pkgName) }
|
||||
catch(e:NameNotFoundException){ false }
|
||||
catch(e:IllegalArgumentException){ false }
|
||||
},
|
||||
{myDpm.setPackagesSuspended(myComponent, arrayOf(pkgName), it)}
|
||||
)
|
||||
}
|
||||
if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
|
||||
SwitchItem(
|
||||
R.string.hide, stringResource(R.string.isapphidden_desc),R.drawable.visibility_off_fill0,
|
||||
{myDpm.isApplicationHidden(myComponent,pkgName)},{myDpm.setApplicationHidden(myComponent, pkgName, it)}
|
||||
)
|
||||
}
|
||||
if(VERSION.SDK_INT>=24&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
|
||||
SwitchItem(
|
||||
R.string.always_on_vpn,"",R.drawable.vpn_key_fill0,{pkgName == myDpm.getAlwaysOnVpnPackage(myComponent)},
|
||||
{
|
||||
try {
|
||||
myDpm.setAlwaysOnVpnPackage(myComponent, pkgName, it)
|
||||
} catch(e: UnsupportedOperationException) {
|
||||
Toast.makeText(myContext, myContext.getString(R.string.unsupported), Toast.LENGTH_SHORT).show()
|
||||
} catch(e: NameNotFoundException) {
|
||||
Toast.makeText(myContext, myContext.getString(R.string.not_installed), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
|
||||
SubPageItem(R.string.block_uninstall,"",R.drawable.delete_forever_fill0){navCtrl.navigate("BlockUninstall")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=30&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
|
||||
SubPageItem(R.string.ucd,"",R.drawable.do_not_touch_fill0){navCtrl.navigate("UserControlDisabled")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=23&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
|
||||
SubPageItem(R.string.permission_manage,"",R.drawable.key_fill0){navCtrl.navigate("PermissionManage")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
|
||||
SubPageItem(R.string.cross_profile_package,"",R.drawable.work_fill0){navCtrl.navigate("CrossProfilePackage")}
|
||||
}
|
||||
if(isProfileOwner(myDpm)){
|
||||
SubPageItem(R.string.cross_profile_widget,"",R.drawable.widgets_fill0){navCtrl.navigate("CrossProfileWidget")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=34&&isDeviceOwner(myDpm)){
|
||||
SubPageItem(R.string.credential_manage_policy,"",R.drawable.license_fill0){navCtrl.navigate("CredentialManagePolicy")}
|
||||
}
|
||||
if(isProfileOwner(myDpm)||isDeviceOwner(myDpm)){
|
||||
SubPageItem(R.string.permitted_accessibility_app,"",R.drawable.settings_accessibility_fill0){navCtrl.navigate("Accessibility")}
|
||||
}
|
||||
if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
|
||||
SubPageItem(R.string.permitted_ime,"",R.drawable.keyboard_fill0){navCtrl.navigate("IME")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=28&&isDeviceOwner(myDpm)){
|
||||
SubPageItem(R.string.keep_uninstalled_pkgs,"",R.drawable.delete_fill0){navCtrl.navigate("KeepUninstalled")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=28){
|
||||
SubPageItem(R.string.clear_app_data,"",R.drawable.mop_fill0){navCtrl.navigate("ClearAppData")}
|
||||
}
|
||||
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")}
|
||||
if(VERSION.SDK_INT>=34){
|
||||
SubPageItem(R.string.set_default_dialer,"",R.drawable.call_fill0){navCtrl.navigate("DefaultDialer")}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun UserCtrlDisabledPkg(pkgName:String){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
var pkgList = myDpm.getUserControlDisabledPackages(myComponent)
|
||||
var listText by remember{mutableStateOf("")}
|
||||
val refresh = {
|
||||
pkgList = myDpm.getUserControlDisabledPackages(myComponent)
|
||||
listText = pkgList.toText()
|
||||
}
|
||||
var inited by remember{mutableStateOf(false)}
|
||||
if(!inited){refresh();inited=true}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.ucd), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text = stringResource(R.string.ucd_desc))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text = stringResource(R.string.app_list_is))
|
||||
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(Animations().animateListSize)){
|
||||
Text(text = if(listText==""){stringResource(R.string.none)}else{listText})
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = {
|
||||
if(pkgName!=""){
|
||||
pkgList.add(pkgName)
|
||||
myDpm.setUserControlDisabledPackages(myComponent,pkgList)
|
||||
refresh()
|
||||
}else{
|
||||
Toast.makeText(myContext, myContext.getString(R.string.fail), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
){
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val result = if(pkgName!=""){pkgList.remove(pkgName)}else{false}
|
||||
if(result){
|
||||
myDpm.setUserControlDisabledPackages(myComponent,pkgList)
|
||||
refresh()
|
||||
}else{
|
||||
Toast.makeText(myContext, myContext.getString(R.string.not_exist), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
){
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = { myDpm.setUserControlDisabledPackages(myComponent, listOf()); refresh() },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.clear_list))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BlockUninstall(pkgName: String){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
var state by remember{mutableStateOf(myDpm.isUninstallBlocked(myComponent,pkgName))}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.block_uninstall), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(stringResource(R.string.current_state, stringResource(if(state){R.string.enabled}else{R.string.disabled})))
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
Text(text = stringResource(R.string.sometimes_get_wrong_block_uninstall_state))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
myDpm.setUninstallBlocked(myComponent,pkgName,true)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
state = myDpm.isUninstallBlocked(myComponent,pkgName)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.enable))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
myDpm.setUninstallBlocked(myComponent,pkgName,false)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
state = myDpm.isUninstallBlocked(myComponent,pkgName)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
){
|
||||
Text(stringResource(R.string.disable))
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun PermissionManage(pkgName: String, navCtrl: NavHostController){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var inputPermission by remember{mutableStateOf(selectedPermission)}
|
||||
var currentState by remember{mutableStateOf(myContext.getString(R.string.unknown))}
|
||||
val grantState = mapOf(
|
||||
PERMISSION_GRANT_STATE_DEFAULT to stringResource(R.string.decide_by_user),
|
||||
PERMISSION_GRANT_STATE_GRANTED to stringResource(R.string.granted),
|
||||
PERMISSION_GRANT_STATE_DENIED to stringResource(R.string.denied)
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
while(true){
|
||||
if(applySelectedPermission){inputPermission = selectedPermission; applySelectedPermission = false}
|
||||
delay(100)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(pkgName) {
|
||||
if(pkgName!=""){currentState = grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)]!!}
|
||||
}
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.permission_manage), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = inputPermission,
|
||||
label = { Text(stringResource(R.string.permission))},
|
||||
onValueChange = {
|
||||
inputPermission = it; selectedPermission = inputPermission
|
||||
currentState = grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)]!!
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth(),
|
||||
trailingIcon = {
|
||||
Icon(painter = painterResource(R.drawable.checklist_fill0), contentDescription = null,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(50))
|
||||
.clickable(onClick = {navCtrl.navigate("PermissionPicker")})
|
||||
.padding(3.dp))
|
||||
}
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(stringResource(R.string.current_state, currentState))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.setPermissionGrantState(myComponent,pkgName,inputPermission, PERMISSION_GRANT_STATE_GRANTED)
|
||||
currentState = grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)]!!
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.grant))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.setPermissionGrantState(myComponent,pkgName,inputPermission, PERMISSION_GRANT_STATE_DENIED)
|
||||
currentState = grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)]!!
|
||||
},
|
||||
Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.deny))
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.setPermissionGrantState(myComponent,pkgName,inputPermission, PERMISSION_GRANT_STATE_DEFAULT)
|
||||
currentState = grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)]!!
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.decide_by_user))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun CrossProfilePkg(pkgName: String){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.cross_profile_package), style = typography.headlineLarge)
|
||||
var list by remember{mutableStateOf("")}
|
||||
val refresh = {
|
||||
crossProfilePkg = myDpm.getCrossProfilePackages(myComponent)
|
||||
list = crossProfilePkg.toText()
|
||||
}
|
||||
LaunchedEffect(Unit){refresh()}
|
||||
Text(text = stringResource(R.string.app_list_is))
|
||||
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(Animations().animateListSize)){
|
||||
Text(text = if(list==""){stringResource(R.string.none)}else{list})
|
||||
}
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = {
|
||||
if(pkgName!=""){
|
||||
crossProfilePkg.add(pkgName)}
|
||||
myDpm.setCrossProfilePackages(myComponent, crossProfilePkg)
|
||||
refresh()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if(pkgName!=""){
|
||||
crossProfilePkg.remove(pkgName)}
|
||||
myDpm.setCrossProfilePackages(myComponent, crossProfilePkg)
|
||||
refresh()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CrossProfileWidget(pkgName: String){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
var pkgList: MutableList<String>
|
||||
var list by remember{mutableStateOf("")}
|
||||
val refresh = {
|
||||
pkgList = myDpm.getCrossProfileWidgetProviders(myComponent)
|
||||
list = pkgList.toText()
|
||||
}
|
||||
LaunchedEffect(Unit){refresh()}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.cross_profile_widget), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text = stringResource(R.string.app_list_is))
|
||||
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(Animations().animateListSize)){
|
||||
Text(text = if(list==""){stringResource(R.string.none)}else{list})
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = {
|
||||
if(pkgName!=""){myDpm.addCrossProfileWidgetProvider(myComponent,pkgName)}
|
||||
refresh()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
){
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if(pkgName!=""){myDpm.removeCrossProfileWidgetProvider(myComponent,pkgName)}
|
||||
refresh()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
){
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun CredentialManagePolicy(pkgName: String){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var policy:PackagePolicy?
|
||||
var policyType by remember{mutableIntStateOf(-1)}
|
||||
var credentialListText by remember{mutableStateOf("")}
|
||||
val refreshPolicy = {
|
||||
policy = myDpm.credentialManagerPolicy
|
||||
policyType = policy?.policyType ?: -1
|
||||
credentialList = policy?.packageNames ?: mutableSetOf()
|
||||
credentialList = credentialList.toMutableSet()
|
||||
}
|
||||
LaunchedEffect(Unit){refreshPolicy(); credentialListText = credentialList.toText()}
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.credential_manage_policy), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
RadioButtonItem(stringResource(R.string.none),{policyType==-1},{policyType=-1})
|
||||
RadioButtonItem(stringResource(R.string.blacklist),{policyType==PACKAGE_POLICY_BLOCKLIST},{policyType=PACKAGE_POLICY_BLOCKLIST})
|
||||
RadioButtonItem(stringResource(R.string.whitelist),{policyType==PACKAGE_POLICY_ALLOWLIST},{policyType=PACKAGE_POLICY_ALLOWLIST})
|
||||
RadioButtonItem(stringResource(R.string.whitelist_and_system_app),{policyType==PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM},{policyType=PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM})
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
AnimatedVisibility(policyType!=-1) {
|
||||
Column {
|
||||
Text(stringResource(R.string.app_list_is))
|
||||
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
|
||||
Text(text = if(credentialListText!=""){ credentialListText }else{ stringResource(R.string.none) })
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = {
|
||||
if(pkgName!=""){
|
||||
credentialList.add(pkgName)}
|
||||
credentialListText = credentialList.toText()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if(pkgName!=""){
|
||||
credentialList.remove(pkgName)}
|
||||
credentialListText = credentialList.toText()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
try{
|
||||
if(policyType!=-1&&credentialList.isNotEmpty()){
|
||||
myDpm.credentialManagerPolicy = PackagePolicy(policyType, credentialList)
|
||||
}else{
|
||||
myDpm.credentialManagerPolicy = null
|
||||
}
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
}catch(e:java.lang.IllegalArgumentException){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.fail), Toast.LENGTH_SHORT).show()
|
||||
}finally {
|
||||
refreshPolicy()
|
||||
credentialListText = credentialList.toText()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PermittedAccessibility(pkgName: String){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
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.permitted_accessibility_app), style = typography.headlineLarge)
|
||||
var listText by remember{ mutableStateOf("") }
|
||||
LaunchedEffect(Unit){
|
||||
val getList = myDpm.getPermittedAccessibilityServices(myComponent)
|
||||
if(getList!=null){ permittedAccessibility = getList }
|
||||
listText = permittedAccessibility.toText()
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text = stringResource(R.string.app_list_is))
|
||||
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(Animations().animateListSize)){
|
||||
Text(text = if(listText==""){stringResource(R.string.none)}else{listText})
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = { permittedAccessibility.add(pkgName); listText = permittedAccessibility.toText()},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
Button(
|
||||
onClick = { permittedAccessibility.remove(pkgName); listText = permittedAccessibility.toText() },
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
Toast.makeText(myContext, if(myDpm.setPermittedAccessibilityServices(myComponent, permittedAccessibility)){"成功"}else{"失败"}, Toast.LENGTH_SHORT).show()
|
||||
val getList = myDpm.getPermittedAccessibilityServices(myComponent)
|
||||
if(getList!=null){ permittedAccessibility = getList }
|
||||
listText = permittedAccessibility.toText()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PermittedIME(pkgName: String){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
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.permitted_ime), style = typography.headlineLarge)
|
||||
var imeListText by remember{ mutableStateOf("") }
|
||||
LaunchedEffect(Unit){
|
||||
val getList = myDpm.getPermittedInputMethods(myComponent)
|
||||
if(getList!=null){ permittedIme = getList }
|
||||
imeListText = permittedIme.toText()
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text = stringResource(R.string.app_list_is))
|
||||
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
|
||||
Text(text = if(imeListText==""){stringResource(R.string.none)}else{imeListText})
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = { permittedIme.add(pkgName); imeListText = permittedIme.toText() },
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
Button(
|
||||
onClick = { permittedIme.remove(pkgName); imeListText = permittedIme.toText() },
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
Toast.makeText(myContext, if(myDpm.setPermittedInputMethods(myComponent, permittedIme)){"成功"}else{"失败"}, Toast.LENGTH_SHORT).show()
|
||||
val getList = myDpm.getPermittedInputMethods(myComponent)
|
||||
if(getList!=null){ permittedIme = getList }
|
||||
imeListText = permittedIme.toText()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun KeepUninstalledApp(pkgName: String){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
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.keep_uninstalled_pkgs), style = typography.headlineLarge)
|
||||
var listText by remember{mutableStateOf("")}
|
||||
LaunchedEffect(Unit){
|
||||
val getList = myDpm.getKeepUninstalledPackages(myComponent)
|
||||
if(getList!=null){ keepUninstallPkg = getList }
|
||||
listText = keepUninstallPkg.toText()
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text = stringResource(R.string.app_list_is))
|
||||
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
|
||||
Text(text = if(listText==""){stringResource(R.string.none)}else{listText})
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = {
|
||||
keepUninstallPkg.add(pkgName)
|
||||
listText = keepUninstallPkg.toText()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
){
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
keepUninstallPkg.remove(pkgName)
|
||||
listText = keepUninstallPkg.toText()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
){
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
myDpm.setKeepUninstalledPackages(myComponent, keepUninstallPkg)
|
||||
val getList = myDpm.getKeepUninstalledPackages(myComponent)
|
||||
if(getList!=null){ keepUninstallPkg = getList }
|
||||
listText = keepUninstallPkg.toText()
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UninstallApp(pkgName: String){
|
||||
val myContext = LocalContext.current
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.uninstall_app), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Column(modifier = Modifier.fillMaxWidth()){
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(myContext, PackageInstallerReceiver::class.java)
|
||||
val intentSender = PendingIntent.getBroadcast(myContext, 8, intent, PendingIntent.FLAG_IMMUTABLE).intentSender
|
||||
val pkgInstaller = myContext.packageManager.packageInstaller
|
||||
pkgInstaller.uninstall(pkgName, intentSender)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.silent_uninstall))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE)
|
||||
intent.setData(Uri.parse("package:$pkgName"))
|
||||
myContext.startActivity(intent)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.request_uninstall))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InstallApp(){
|
||||
val myContext = LocalContext.current
|
||||
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.install_app), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
val installApkIntent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
installApkIntent.setType("application/vnd.android.package-archive")
|
||||
installApkIntent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
getApk.launch(installApkIntent)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.select_apk))
|
||||
}
|
||||
var selected by remember{mutableStateOf(false)}
|
||||
LaunchedEffect(selected){while(true){ delay(800); selected = apkUri!=null}}
|
||||
AnimatedVisibility(selected) {
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
Column(modifier = Modifier.fillMaxWidth()){
|
||||
Button(
|
||||
onClick = { uriToStream(myContext, apkUri){stream -> installPackage(myContext,stream)} },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.silent_install))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
|
||||
intent.setData(apkUri)
|
||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
myContext.startActivity(intent)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.request_install))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun ClearAppData(pkgName: String){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
val executor = Executors.newCachedThreadPool()
|
||||
val onClear = DevicePolicyManager.OnClearApplicationUserDataListener { pkg: String, succeed: Boolean ->
|
||||
Looper.prepare()
|
||||
focusMgr.clearFocus()
|
||||
val toastText = if(pkg!=""){"$pkg\n"}else{""} + myContext.getString(R.string.clear_data) + myContext.getString(if(succeed){R.string.success}else{R.string.fail})
|
||||
Toast.makeText(myContext, toastText, Toast.LENGTH_SHORT).show()
|
||||
Looper.loop()
|
||||
}
|
||||
myDpm.clearApplicationUserData(myComponent,pkgName,executor,onClear)
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 10.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.clear_app_data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun DefaultDialerApp(pkgName: String){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
try{
|
||||
myDpm.setDefaultDialerApplication(pkgName)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
}catch(e:IllegalArgumentException){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.fail), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 10.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.set_default_dialer))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun installPackage(context: Context, inputStream: InputStream){
|
||||
val packageInstaller = context.packageManager.packageInstaller
|
||||
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||
val sessionId = packageInstaller.createSession(params)
|
||||
val session = packageInstaller.openSession(sessionId)
|
||||
val out = session.openWrite("COSU", 0, -1)
|
||||
val buffer = ByteArray(65536)
|
||||
var c: Int
|
||||
while(inputStream.read(buffer).also{c = it}!=-1) { out.write(buffer, 0, c) }
|
||||
session.fsync(out)
|
||||
inputStream.close()
|
||||
out.close()
|
||||
val pendingIntent = PendingIntent.getBroadcast(context, sessionId, Intent(context, PackageInstallerReceiver::class.java), PendingIntent.FLAG_IMMUTABLE).intentSender
|
||||
session.commit(pendingIntent)
|
||||
}
|
||||
28
app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt
Normal file
28
app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
|
||||
var selectedPackage = ""
|
||||
var applySelectedPackage = false
|
||||
var selectedPermission = ""
|
||||
var applySelectedPermission = false
|
||||
lateinit var getCaCert: ActivityResultLauncher<Intent>
|
||||
lateinit var createManagedProfile: ActivityResultLauncher<Intent>
|
||||
lateinit var getApk: ActivityResultLauncher<Intent>
|
||||
lateinit var getUserIcon: ActivityResultLauncher<Intent>
|
||||
|
||||
var userIconUri: Uri? = null
|
||||
var apkUri: Uri? = null
|
||||
var caCert = byteArrayOf()
|
||||
|
||||
fun isDeviceOwner(dpm: DevicePolicyManager): Boolean {
|
||||
return dpm.isDeviceOwnerApp("com.bintianqi.owndroid")
|
||||
}
|
||||
|
||||
fun isProfileOwner(dpm: DevicePolicyManager): Boolean {
|
||||
return dpm.isProfileOwnerApp("com.bintianqi.owndroid")
|
||||
}
|
||||
|
||||
289
app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt
Normal file
289
app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt
Normal file
@@ -0,0 +1,289 @@
|
||||
package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.app.admin.DevicePolicyManager.*
|
||||
import android.content.*
|
||||
import android.os.Binder
|
||||
import android.os.Build.VERSION
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.focusable
|
||||
import androidx.compose.foundation.layout.*
|
||||
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.*
|
||||
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.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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.Receiver
|
||||
import com.bintianqi.owndroid.ui.*
|
||||
import com.bintianqi.owndroid.ui.theme.bgColor
|
||||
|
||||
@Composable
|
||||
fun ManagedProfile(navCtrl: NavHostController) {
|
||||
val localNavCtrl = rememberNavController()
|
||||
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
|
||||
/*val titleMap = mapOf(
|
||||
"OrgOwnedWorkProfile" to R.string.org_owned_work_profile,
|
||||
"CreateWorkProfile" to R.string.create_work_profile,
|
||||
"SuspendPersonalApp" to R.string.suspend_personal_app,
|
||||
"IntentFilter" to R.string.intent_filter,
|
||||
"OrgID" to R.string.org_id
|
||||
)*/
|
||||
Scaffold(
|
||||
topBar = {
|
||||
/*TopAppBar(
|
||||
title = {Text(text = stringResource(titleMap[backStackEntry?.destination?.route]?:R.string.work_profile))},
|
||||
navigationIcon = {NavIcon{if(backStackEntry?.destination?.route=="Home"){navCtrl.navigateUp()}else{localNavCtrl.navigateUp()}}},
|
||||
colors = TopAppBarDefaults.topAppBarColors(containerColor = colorScheme.surfaceVariant)
|
||||
)*/
|
||||
TopBar(backStackEntry,navCtrl, localNavCtrl)
|
||||
}
|
||||
){
|
||||
NavHost(
|
||||
navController = localNavCtrl, startDestination = "Home",
|
||||
enterTransition = Animations().navHostEnterTransition,
|
||||
exitTransition = Animations().navHostExitTransition,
|
||||
popEnterTransition = Animations().navHostPopEnterTransition,
|
||||
popExitTransition = Animations().navHostPopExitTransition,
|
||||
modifier = Modifier.background(bgColor).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 = "OrgID"){OrgID()}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Home(navCtrl: NavHostController){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext, Receiver::class.java)
|
||||
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 = 15.dp))
|
||||
if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
|
||||
SubPageItem(R.string.org_owned_work_profile,"",R.drawable.corporate_fare_fill0){navCtrl.navigate("OrgOwnedWorkProfile")}
|
||||
}
|
||||
if(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE))){
|
||||
SubPageItem(R.string.create_work_profile,"",R.drawable.work_fill0){navCtrl.navigate("CreateWorkProfile")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile){
|
||||
SubPageItem(R.string.suspend_personal_app,"",R.drawable.block_fill0){navCtrl.navigate("SuspendPersonalApp")}
|
||||
}
|
||||
if(isProfileOwner(myDpm)&&(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isManagedProfile(myComponent)))){
|
||||
SubPageItem(R.string.intent_filter,"",R.drawable.filter_alt_fill0){navCtrl.navigate("IntentFilter")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=31&&(isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent))){
|
||||
SubPageItem(R.string.org_id,"",R.drawable.corporate_fare_fill0){navCtrl.navigate("OrgID")}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CreateWorkProfile(){
|
||||
val myContext = LocalContext.current
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
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))
|
||||
var skipEncrypt by remember{mutableStateOf(false)}
|
||||
if(VERSION.SDK_INT>=24){
|
||||
CheckBoxItem(stringResource(R.string.skip_encryption),{skipEncrypt},{skipEncrypt=!skipEncrypt})
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
try {
|
||||
val intent = Intent(ACTION_PROVISION_MANAGED_PROFILE)
|
||||
if(VERSION.SDK_INT>=23){
|
||||
intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,myComponent)
|
||||
}else{
|
||||
intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME, myContext.packageName)
|
||||
}
|
||||
if(VERSION.SDK_INT>=24){intent.putExtra(EXTRA_PROVISIONING_SKIP_ENCRYPTION,skipEncrypt)}
|
||||
if(VERSION.SDK_INT>=33){intent.putExtra(EXTRA_PROVISIONING_ALLOW_OFFLINE,true)}
|
||||
createManagedProfile.launch(intent)
|
||||
}catch(e:ActivityNotFoundException){
|
||||
Toast.makeText(myContext,myContext.getString(R.string.unsupported),Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.create))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun OrgOwnedProfile(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
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,myDpm.isOrganizationOwnedDeviceWithManagedProfile))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(!myDpm.isOrganizationOwnedDeviceWithManagedProfile){
|
||||
SelectionContainer {
|
||||
Text(
|
||||
text = stringResource(R.string.activate_org_profile_command, Binder.getCallingUid()/100000),
|
||||
color = colorScheme.onTertiaryContainer
|
||||
)
|
||||
}
|
||||
CopyTextButton(myContext, R.string.copy_command, stringResource(R.string.activate_org_profile_command, Binder.getCallingUid()/100000))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun OrgID(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val focusMgr = LocalFocusManager.current
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
var orgId by remember{mutableStateOf("")}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.org_id), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = orgId, onValueChange = {orgId=it},
|
||||
label = {Text(stringResource(R.string.org_id))},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 2.dp))
|
||||
AnimatedVisibility(orgId.length !in 6..64) {
|
||||
Text(text = stringResource(R.string.length_6_to_64))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.setOrganizationId(orgId)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success),Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = orgId.length in 6..64,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Information{Text(text = stringResource(R.string.get_specific_id_after_set_org_id))}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun SuspendPersonalApp(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
SwitchItem(
|
||||
R.string.suspend_personal_app,"",null,{myDpm.getPersonalAppsSuspendedReasons(myComponent)!=PERSONAL_APPS_NOT_SUSPENDED},
|
||||
{myDpm.setPersonalAppsSuspended(myComponent,it)}
|
||||
)
|
||||
var time by remember{mutableStateOf("")}
|
||||
time = myDpm.getManagedProfileMaximumTimeOff(myComponent).toString()
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.profile_max_time_off), style = typography.titleLarge)
|
||||
Text(text = stringResource(R.string.profile_max_time_out_desc))
|
||||
Text(text = stringResource(R.string.personal_app_suspended_because_timeout, myDpm.getPersonalAppsSuspendedReasons(myComponent)==PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT))
|
||||
OutlinedTextField(
|
||||
value = time, onValueChange = {time=it}, modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.dp),
|
||||
label = {Text(stringResource(R.string.time_unit_ms))},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
|
||||
)
|
||||
Text(text = stringResource(R.string.cannot_less_than_72_hours))
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.setManagedProfileMaximumTimeOff(myComponent,time.toLong())
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun IntentFilter(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
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")},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.addCrossProfileIntentFilter(myComponent, IntentFilter(action), FLAG_PARENT_CAN_ACCESS_MANAGED)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success),Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.add_intent_filter_work_to_personal))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.addCrossProfileIntentFilter(myComponent, IntentFilter(action), FLAG_MANAGED_CAN_ACCESS_PARENT)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success),Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.add_intent_filter_personal_to_work))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 2.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.clearCrossProfileIntentFilters(myComponent)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success),Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.clear_cross_profile_filters))
|
||||
}
|
||||
}
|
||||
}
|
||||
809
app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt
Normal file
809
app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt
Normal file
@@ -0,0 +1,809 @@
|
||||
package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.app.admin.DevicePolicyManager.*
|
||||
import android.app.admin.WifiSsidPolicy
|
||||
import android.app.admin.WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST
|
||||
import android.app.admin.WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST
|
||||
import android.content.ComponentName
|
||||
import android.net.wifi.WifiSsid
|
||||
import android.os.Build.VERSION
|
||||
import android.telephony.TelephonyManager
|
||||
import android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID
|
||||
import android.telephony.data.ApnSetting.*
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
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.dpm.scrollAnim
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.Receiver
|
||||
import com.bintianqi.owndroid.toText
|
||||
import com.bintianqi.owndroid.ui.*
|
||||
import com.bintianqi.owndroid.ui.theme.bgColor
|
||||
|
||||
var ssidSet = mutableSetOf<WifiSsid>()
|
||||
@Composable
|
||||
fun Network(navCtrl: NavHostController){
|
||||
val localNavCtrl = rememberNavController()
|
||||
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
|
||||
val scrollState = rememberScrollState()
|
||||
/*val titleMap = mapOf(
|
||||
"Home" to R.string.network,
|
||||
"MinWifiSecurityLevel" to R.string.min_wifi_security_level,
|
||||
"WifiSsidPolicy" to R.string.wifi_ssid_policy,
|
||||
"PrivateDNS" to R.string.private_dns,
|
||||
"NetLog" to R.string.retrieve_net_logs,
|
||||
"WifiKeypair" to R.string.wifi_keypair,
|
||||
"APN" to R.string.apn_settings
|
||||
)*/
|
||||
Scaffold(
|
||||
topBar = {
|
||||
/*TopAppBar(
|
||||
title = {Text(text = stringResource(titleMap[backStackEntry?.destination?.route]?:R.string.network))},
|
||||
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.network),
|
||||
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.background(bgColor).padding(top = it.calculateTopPadding())
|
||||
){
|
||||
composable(route = "Home"){Home(localNavCtrl,scrollState)}
|
||||
composable(route = "Switches"){Switches()}
|
||||
composable(route = "MinWifiSecurityLevel"){WifiSecLevel()}
|
||||
composable(route = "WifiSsidPolicy"){WifiSsidPolicy()}
|
||||
composable(route = "PrivateDNS"){PrivateDNS()}
|
||||
composable(route = "NetLog"){NetLog()}
|
||||
composable(route = "WifiKeypair"){WifiKeypair()}
|
||||
composable(route = "APN"){APN()}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Home(navCtrl:NavHostController,scrollState: ScrollState){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext, Receiver::class.java)
|
||||
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 = 15.dp))
|
||||
if(VERSION.SDK_INT>=24&&isDeviceOwner(myDpm)){
|
||||
val wifimac = myDpm.getWifiMacAddress(myComponent)
|
||||
Text(text = "WiFi MAC: $wifimac", modifier = Modifier.padding(start = 15.dp))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
if(VERSION.SDK_INT>=30){
|
||||
SubPageItem(R.string.options,"",R.drawable.tune_fill0){navCtrl.navigate("Switches")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=33){
|
||||
SubPageItem(R.string.min_wifi_security_level,"",R.drawable.wifi_password_fill0){navCtrl.navigate("MinWifiSecurityLevel")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=33&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
|
||||
SubPageItem(R.string.wifi_ssid_policy,"",R.drawable.wifi_fill0){navCtrl.navigate("WifiSsidPolicy")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=29&&isDeviceOwner(myDpm)){
|
||||
SubPageItem(R.string.private_dns,"",R.drawable.dns_fill0){navCtrl.navigate("PrivateDNS")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=26&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)))){
|
||||
SubPageItem(R.string.retrieve_net_logs,"",R.drawable.description_fill0){navCtrl.navigate("NetLog")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=31&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
|
||||
SubPageItem(R.string.wifi_keypair,"",R.drawable.key_fill0){navCtrl.navigate("WifiKeypair")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=28&&isDeviceOwner(myDpm)){
|
||||
SubPageItem(R.string.apn_settings,"",R.drawable.cell_tower_fill0){navCtrl.navigate("APN")}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Switches(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
Column(modifier = Modifier.fillMaxSize()){
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(VERSION.SDK_INT>=33&&isDeviceOwner(myDpm)){
|
||||
SwitchItem(
|
||||
R.string.preferential_network_service, stringResource(R.string.developing),R.drawable.globe_fill0,
|
||||
{myDpm.isPreferentialNetworkServiceEnabled},{myDpm.isPreferentialNetworkServiceEnabled = it}
|
||||
)
|
||||
}
|
||||
if(VERSION.SDK_INT>=30&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
|
||||
SwitchItem(R.string.wifi_lockdown,"",R.drawable.wifi_password_fill0,
|
||||
{myDpm.hasLockdownAdminConfiguredNetworks(myComponent)},{myDpm.setConfiguredNetworksLockdownState(myComponent,it)}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun WifiSecLevel(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
var selectedWifiSecLevel by remember{mutableIntStateOf(myDpm.minimumRequiredWifiSecurityLevel)}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.min_wifi_security_level), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
RadioButtonItem(stringResource(R.string.wifi_security_level_open), {selectedWifiSecLevel==WIFI_SECURITY_OPEN}, {selectedWifiSecLevel= WIFI_SECURITY_OPEN})
|
||||
RadioButtonItem("WEP, WPA(2)-PSK", {selectedWifiSecLevel==WIFI_SECURITY_PERSONAL}, {selectedWifiSecLevel= WIFI_SECURITY_PERSONAL})
|
||||
RadioButtonItem("WPA-EAP", {selectedWifiSecLevel==WIFI_SECURITY_ENTERPRISE_EAP}, {selectedWifiSecLevel= WIFI_SECURITY_ENTERPRISE_EAP})
|
||||
RadioButtonItem("WPA3-192bit", {selectedWifiSecLevel==WIFI_SECURITY_ENTERPRISE_192}, {selectedWifiSecLevel= WIFI_SECURITY_ENTERPRISE_192})
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
enabled = isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile),
|
||||
onClick = {
|
||||
myDpm.minimumRequiredWifiSecurityLevel=selectedWifiSecLevel
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun WifiSsidPolicy(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val focusMgr = LocalFocusManager.current
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
var policy = myDpm.wifiSsidPolicy
|
||||
var selectedPolicyType by remember{mutableIntStateOf(policy?.policyType ?: -1)}
|
||||
var inputSsid by remember{mutableStateOf("")}
|
||||
var ssidList by remember{mutableStateOf("")}
|
||||
val refreshPolicy = {
|
||||
policy = myDpm.wifiSsidPolicy
|
||||
selectedPolicyType = policy?.policyType ?: -1
|
||||
ssidSet = policy?.ssids ?: mutableSetOf()
|
||||
}
|
||||
LaunchedEffect(Unit){refreshPolicy(); ssidList= ssidSet.toText()}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.wifi_ssid_policy), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
RadioButtonItem(stringResource(R.string.none),{selectedPolicyType==-1},{selectedPolicyType=-1})
|
||||
RadioButtonItem(stringResource(R.string.whitelist),{selectedPolicyType==WIFI_SSID_POLICY_TYPE_ALLOWLIST},{selectedPolicyType=WIFI_SSID_POLICY_TYPE_ALLOWLIST})
|
||||
RadioButtonItem(stringResource(R.string.blacklist),{selectedPolicyType==WIFI_SSID_POLICY_TYPE_DENYLIST},{selectedPolicyType=WIFI_SSID_POLICY_TYPE_DENYLIST})
|
||||
Column(modifier = Modifier.animateContentSize(scrollAnim()).horizontalScroll(rememberScrollState())){
|
||||
if(ssidList!=""){
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(stringResource(R.string.ssid_list_is))
|
||||
SelectionContainer{
|
||||
Text(text = ssidList, color = colorScheme.onPrimaryContainer)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = inputSsid,
|
||||
label = { Text("SSID")},
|
||||
onValueChange = {inputSsid = it},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = {
|
||||
if(inputSsid==""){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.cannot_be_empty), Toast.LENGTH_SHORT).show()
|
||||
}else if(WifiSsid.fromBytes(inputSsid.toByteArray()) in ssidSet){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.already_exist), Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
ssidSet.add(WifiSsid.fromBytes(inputSsid.toByteArray()))
|
||||
ssidList = ssidSet.toText()
|
||||
}
|
||||
inputSsid = ""
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if(inputSsid==""){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.cannot_be_empty), Toast.LENGTH_SHORT).show()
|
||||
}else if(WifiSsid.fromBytes(inputSsid.toByteArray()) in ssidSet){
|
||||
ssidSet.remove(WifiSsid.fromBytes(inputSsid.toByteArray()))
|
||||
inputSsid = ""
|
||||
ssidList = ssidSet.toText()
|
||||
}else{
|
||||
Toast.makeText(myContext, myContext.getString(R.string.not_exist), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
if(selectedPolicyType==-1){
|
||||
if(policy==null&&ssidSet.isNotEmpty()){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.please_select_a_policy), Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
myDpm.wifiSsidPolicy = null
|
||||
refreshPolicy()
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}else{
|
||||
myDpm.wifiSsidPolicy = if(ssidSet.size==0){ null }else{ WifiSsidPolicy(selectedPolicyType, ssidSet) }
|
||||
refreshPolicy()
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun PrivateDNS(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
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)
|
||||
val dnsStatus = mapOf(
|
||||
PRIVATE_DNS_MODE_UNKNOWN to stringResource(R.string.unknown),
|
||||
PRIVATE_DNS_MODE_OFF to stringResource(R.string.disabled),
|
||||
PRIVATE_DNS_MODE_OPPORTUNISTIC to stringResource(R.string.auto),
|
||||
PRIVATE_DNS_MODE_PROVIDER_HOSTNAME to stringResource(R.string.dns_provide_hostname)
|
||||
)
|
||||
val operationResult = mapOf(
|
||||
PRIVATE_DNS_SET_NO_ERROR to stringResource(R.string.success),
|
||||
PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING to stringResource(R.string.host_not_serving_dns_tls),
|
||||
PRIVATE_DNS_SET_ERROR_FAILURE_SETTING to stringResource(R.string.fail)
|
||||
)
|
||||
var status by remember{mutableStateOf(dnsStatus[myDpm.getGlobalPrivateDnsMode(myComponent)])}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text = stringResource(R.string.current_state, status?:stringResource(R.string.unknown)))
|
||||
AnimatedVisibility(visible = myDpm.getGlobalPrivateDnsMode(myComponent)!=PRIVATE_DNS_MODE_OPPORTUNISTIC) {
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
val result = myDpm.setGlobalPrivateDnsModeOpportunistic(myComponent)
|
||||
Toast.makeText(myContext, operationResult[result], Toast.LENGTH_SHORT).show()
|
||||
status = dnsStatus[myDpm.getGlobalPrivateDnsMode(myComponent)]
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.set_to_auto))
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
var inputHost by remember{mutableStateOf(myDpm.getGlobalPrivateDnsHost(myComponent) ?: "")}
|
||||
OutlinedTextField(
|
||||
value = inputHost,
|
||||
onValueChange = {inputHost=it},
|
||||
label = {Text(stringResource(R.string.dns_hostname))},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
val result: Int
|
||||
try{
|
||||
result = myDpm.setGlobalPrivateDnsModeSpecifiedHost(myComponent,inputHost)
|
||||
Toast.makeText(myContext, operationResult[result], Toast.LENGTH_SHORT).show()
|
||||
}catch(e:IllegalArgumentException){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.invalid_hostname), Toast.LENGTH_SHORT).show()
|
||||
}catch(e:SecurityException){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.security_exception), Toast.LENGTH_SHORT).show()
|
||||
}finally {
|
||||
status = dnsStatus[myDpm.getGlobalPrivateDnsMode(myComponent)]
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.set_dns_host))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun NetLog(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
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))
|
||||
Text(text = stringResource(R.string.developing))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
SwitchItem(R.string.enable,"",null,{myDpm.isNetworkLoggingEnabled(myComponent)},{myDpm.setNetworkLoggingEnabled(myComponent,it)})
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
val log = myDpm.retrieveNetworkLogs(myComponent,1234567890)
|
||||
if(log!=null){
|
||||
for(i in log){ Log.d("NetLog",i.toString()) }
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success),Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
Log.d("NetLog",myContext.getString(R.string.none))
|
||||
Toast.makeText(myContext, myContext.getString(R.string.none),Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.retrieve))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun WifiKeypair(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
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_keypair), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = keyPair,
|
||||
label = { Text(stringResource(R.string.keypair))},
|
||||
onValueChange = {keyPair = it},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
val isExist = try{myDpm.isKeyPairGrantedToWifiAuth(keyPair)}catch(e:java.lang.IllegalArgumentException){false}
|
||||
Text(stringResource(R.string.already_exist)+":$isExist")
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = {
|
||||
val result = myDpm.grantKeyPairToWifiAuth(keyPair)
|
||||
Toast.makeText(myContext, myContext.getString(if(result){R.string.success}else{R.string.fail}), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val result = myDpm.revokeKeyPairFromWifiAuth(keyPair)
|
||||
Toast.makeText(myContext, myContext.getString(if(result){R.string.success}else{R.string.fail}), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun APN(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
val setting = myDpm.getOverrideApns(myComponent)
|
||||
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.apn_settings), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text = stringResource(id = R.string.developing))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
SwitchItem(R.string.enable,"",null,{myDpm.isOverrideApnEnabled(myComponent)},{myDpm.setOverrideApnsEnabled(myComponent,it)})
|
||||
Text(text = stringResource(R.string.total_apn_amount, setting.size))
|
||||
if(setting.size>0){
|
||||
Text(text = stringResource(R.string.select_a_apn_or_create, setting.size))
|
||||
TextField(
|
||||
value = inputNum,
|
||||
label = { Text("APN")},
|
||||
onValueChange = {inputNum = it},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.dp),
|
||||
enabled = !nextStep
|
||||
)
|
||||
}else{
|
||||
Text(text = stringResource(R.string.no_apn_you_should_create_one))
|
||||
}
|
||||
Button(
|
||||
onClick = {focusMgr.clearFocus(); nextStep=!nextStep},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = inputNum!=""&&(nextStep||inputNum=="0"||setting[inputNum.toInt()-1]!=null)
|
||||
) {
|
||||
Text(stringResource(if(nextStep){R.string.previous_step}else{R.string.next_step}))
|
||||
}
|
||||
var result = Builder().build()
|
||||
AnimatedVisibility(nextStep) {
|
||||
var carrierEnabled by remember{mutableStateOf(false)}
|
||||
var inputApnName by remember{mutableStateOf("")}
|
||||
var user by remember{mutableStateOf("")}
|
||||
var profileId by remember{mutableStateOf("")}
|
||||
var selectedAuthType by remember{mutableIntStateOf(AUTH_TYPE_NONE)}
|
||||
var carrierId by remember{mutableStateOf("$UNKNOWN_CARRIER_ID")}
|
||||
var apnTypeBitmask by remember{mutableStateOf("")}
|
||||
var entryName by remember{mutableStateOf("")}
|
||||
var mmsProxyAddress by remember{mutableStateOf("")}
|
||||
var mmsProxyPort by remember{mutableStateOf("")}
|
||||
var proxyAddress by remember{mutableStateOf("")}
|
||||
var proxyPort by remember{mutableStateOf("")}
|
||||
var mmsc by remember{mutableStateOf("")}
|
||||
var mtuV4 by remember{mutableStateOf("")}
|
||||
var mtuV6 by remember{mutableStateOf("")}
|
||||
var mvnoType by remember{mutableIntStateOf(-1)}
|
||||
var networkTypeBitmask by remember{mutableStateOf("")}
|
||||
var operatorNumeric by remember{mutableStateOf("")}
|
||||
var password by remember{mutableStateOf("")}
|
||||
var persistent by remember{mutableStateOf(false)}
|
||||
var protocol by remember{mutableIntStateOf(-1)}
|
||||
var roamingProtocol by remember{mutableIntStateOf(-1)}
|
||||
var id by remember{mutableIntStateOf(0)}
|
||||
|
||||
if(inputNum!="0"){
|
||||
val current = setting[inputNum.toInt()-1]
|
||||
id = current.id
|
||||
carrierEnabled = current.isEnabled
|
||||
inputApnName = current.apnName
|
||||
user = current.user
|
||||
if(VERSION.SDK_INT>=33){profileId = current.profileId.toString()}
|
||||
selectedAuthType = current.authType
|
||||
apnTypeBitmask = current.apnTypeBitmask.toString()
|
||||
entryName = current.entryName
|
||||
if(VERSION.SDK_INT>=29){mmsProxyAddress = current.mmsProxyAddressAsString}
|
||||
mmsProxyPort = current.mmsProxyPort.toString()
|
||||
if(VERSION.SDK_INT>=29){proxyAddress = current.proxyAddressAsString}
|
||||
proxyPort = current.proxyPort.toString()
|
||||
mmsc = current.mmsc.toString()
|
||||
if(VERSION.SDK_INT>=33){ mtuV4 = current.mtuV4.toString(); mtuV6 = current.mtuV6.toString() }
|
||||
mvnoType = current.mvnoType
|
||||
networkTypeBitmask = current.networkTypeBitmask.toString()
|
||||
operatorNumeric = current.operatorNumeric
|
||||
password = current.password
|
||||
if(VERSION.SDK_INT>=33){persistent = current.isPersistent}
|
||||
protocol = current.protocol
|
||||
roamingProtocol = current.roamingProtocol
|
||||
}
|
||||
|
||||
Column {
|
||||
|
||||
Text(text = "APN", style = typography.titleLarge)
|
||||
TextField(
|
||||
value = inputApnName,
|
||||
onValueChange = {inputApnName=it},
|
||||
label = {Text(stringResource(R.string.name))},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically){
|
||||
Text(text = stringResource(R.string.enable), style = typography.titleLarge)
|
||||
Switch(checked = carrierEnabled, onCheckedChange = {carrierEnabled=it})
|
||||
}
|
||||
|
||||
Text(text = stringResource(R.string.user_name), style = typography.titleLarge)
|
||||
TextField(
|
||||
value = user,
|
||||
onValueChange = {user=it},
|
||||
label = {Text(stringResource(R.string.user_name))},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
if(VERSION.SDK_INT>=33){
|
||||
Text(text = stringResource(R.string.profile_id), style = typography.titleLarge)
|
||||
TextField(
|
||||
value = profileId,
|
||||
onValueChange = {profileId=it},
|
||||
label = {Text("ID")},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Text(text = stringResource(R.string.auth_type), style = typography.titleLarge)
|
||||
RadioButtonItem("无",{selectedAuthType==AUTH_TYPE_NONE},{selectedAuthType=AUTH_TYPE_NONE})
|
||||
RadioButtonItem("CHAP",{selectedAuthType==AUTH_TYPE_CHAP},{selectedAuthType=AUTH_TYPE_CHAP})
|
||||
RadioButtonItem("PAP",{selectedAuthType==AUTH_TYPE_PAP},{selectedAuthType=AUTH_TYPE_PAP})
|
||||
RadioButtonItem("PAP/CHAP",{selectedAuthType==AUTH_TYPE_PAP_OR_CHAP},{selectedAuthType=AUTH_TYPE_PAP_OR_CHAP})
|
||||
|
||||
if(VERSION.SDK_INT>=29){
|
||||
val ts = myContext.getSystemService(ComponentActivity.TELEPHONY_SERVICE) as TelephonyManager
|
||||
carrierId = ts.simCarrierId.toString()
|
||||
Text(text = "CarrierID", style = typography.titleLarge)
|
||||
TextField(
|
||||
value = carrierId,
|
||||
onValueChange = {carrierId=it},
|
||||
label = {Text("ID")},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Text(text = stringResource(R.string.apn_type), style = typography.titleLarge)
|
||||
TextField(
|
||||
value = apnTypeBitmask,
|
||||
onValueChange = {apnTypeBitmask=it},
|
||||
label = {Text(stringResource(R.string.bitmask))},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
Text(text = stringResource(R.string.description), style = typography.titleLarge)
|
||||
TextField(
|
||||
value = entryName,
|
||||
onValueChange = {entryName=it},
|
||||
label = {Text(stringResource(R.string.description))},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
Text(text = stringResource(R.string.mms_proxy), style = typography.titleLarge)
|
||||
if(VERSION.SDK_INT>=29){
|
||||
TextField(
|
||||
value = mmsProxyAddress,
|
||||
onValueChange = {mmsProxyAddress=it},
|
||||
label = {Text(stringResource(R.string.address))},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
}
|
||||
TextField(
|
||||
value = mmsProxyPort,
|
||||
onValueChange = {mmsProxyPort=it},
|
||||
label = {Text(stringResource(R.string.port))},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
Text(text = stringResource(R.string.proxy), style = typography.titleLarge)
|
||||
if(VERSION.SDK_INT>=29){
|
||||
TextField(
|
||||
value = proxyAddress,
|
||||
onValueChange = {proxyAddress=it},
|
||||
label = {Text(stringResource(R.string.address))},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
}
|
||||
TextField(
|
||||
value = proxyPort,
|
||||
onValueChange = {proxyPort=it},
|
||||
label = {Text(stringResource(R.string.port))},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
Text(text = "MMSC", style = typography.titleLarge)
|
||||
TextField(
|
||||
value = mmsc,
|
||||
onValueChange = {mmsc=it},
|
||||
label = {Text("Uri")},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
if(VERSION.SDK_INT>=33){
|
||||
Text(text = "MTU", style = typography.titleLarge)
|
||||
TextField(
|
||||
value = mtuV4,
|
||||
onValueChange = {mtuV4=it},
|
||||
label = {Text("IPV4")},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
TextField(
|
||||
value = mtuV6,
|
||||
onValueChange = {mtuV6=it},
|
||||
label = {Text("IPV6")},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Text(text = "MVNO", style = typography.titleLarge)
|
||||
RadioButtonItem("SPN",{mvnoType==MVNO_TYPE_SPN},{mvnoType=MVNO_TYPE_SPN})
|
||||
RadioButtonItem("IMSI",{mvnoType==MVNO_TYPE_IMSI},{mvnoType=MVNO_TYPE_IMSI})
|
||||
RadioButtonItem("GID",{mvnoType==MVNO_TYPE_GID},{mvnoType=MVNO_TYPE_GID})
|
||||
RadioButtonItem("ICCID",{mvnoType==MVNO_TYPE_ICCID},{mvnoType=MVNO_TYPE_ICCID})
|
||||
|
||||
Text(text = stringResource(R.string.network_type), style = typography.titleLarge)
|
||||
TextField(
|
||||
value = networkTypeBitmask,
|
||||
onValueChange = {networkTypeBitmask=it},
|
||||
label = {Text(stringResource(R.string.bitmask))},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
Text(text = "OperatorNumeric", style = typography.titleLarge)
|
||||
TextField(
|
||||
value = operatorNumeric,
|
||||
onValueChange = {operatorNumeric=it},
|
||||
label = {Text("ID")},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
Text(text = stringResource(R.string.password), style = typography.titleLarge)
|
||||
TextField(
|
||||
value = password,
|
||||
onValueChange = {password=it},
|
||||
label = {Text(stringResource(R.string.password))},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
if(VERSION.SDK_INT>=33){
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically){
|
||||
Text(text = stringResource(R.string.persistent), style = typography.titleLarge)
|
||||
Switch(checked = persistent, onCheckedChange = {persistent=it})
|
||||
}
|
||||
}
|
||||
|
||||
Text(text = stringResource(R.string.protocol), style = typography.titleLarge)
|
||||
RadioButtonItem("IPV4",{protocol==PROTOCOL_IP},{protocol=PROTOCOL_IP})
|
||||
RadioButtonItem("IPV6",{protocol==PROTOCOL_IPV6},{protocol=PROTOCOL_IPV6})
|
||||
RadioButtonItem("IPV4/IPV6",{protocol==PROTOCOL_IPV4V6},{protocol=PROTOCOL_IPV4V6})
|
||||
RadioButtonItem("PPP",{protocol==PROTOCOL_PPP},{protocol=PROTOCOL_PPP})
|
||||
if(VERSION.SDK_INT>=29){
|
||||
RadioButtonItem("non-IP",{protocol==PROTOCOL_NON_IP},{protocol=PROTOCOL_NON_IP})
|
||||
RadioButtonItem("Unstructured",{protocol==PROTOCOL_UNSTRUCTURED},{protocol=PROTOCOL_UNSTRUCTURED})
|
||||
}
|
||||
|
||||
Text(text = stringResource(R.string.roaming_protocol), style = typography.titleLarge)
|
||||
RadioButtonItem("IPV4",{roamingProtocol==PROTOCOL_IP},{roamingProtocol=PROTOCOL_IP})
|
||||
RadioButtonItem("IPV6",{roamingProtocol==PROTOCOL_IPV6},{roamingProtocol=PROTOCOL_IPV6})
|
||||
RadioButtonItem("IPV4/IPV6",{roamingProtocol==PROTOCOL_IPV4V6},{roamingProtocol=PROTOCOL_IPV4V6})
|
||||
RadioButtonItem("PPP",{roamingProtocol==PROTOCOL_PPP},{roamingProtocol=PROTOCOL_PPP})
|
||||
if(VERSION.SDK_INT>=29){
|
||||
RadioButtonItem("non-IP",{roamingProtocol==PROTOCOL_NON_IP},{roamingProtocol=PROTOCOL_NON_IP})
|
||||
RadioButtonItem("Unstructured",{roamingProtocol==PROTOCOL_UNSTRUCTURED},{roamingProtocol=PROTOCOL_UNSTRUCTURED})
|
||||
}
|
||||
|
||||
var finalStep by remember{mutableStateOf(false)}
|
||||
Button(
|
||||
onClick = {
|
||||
if(!finalStep){
|
||||
builder.setCarrierEnabled(carrierEnabled)
|
||||
builder.setApnName(inputApnName)
|
||||
builder.setUser(user)
|
||||
if(VERSION.SDK_INT>=33){builder.setProfileId(profileId.toInt())}
|
||||
builder.setAuthType(selectedAuthType)
|
||||
if(VERSION.SDK_INT>=29){builder.setCarrierId(carrierId.toInt())}
|
||||
builder.setApnTypeBitmask(apnTypeBitmask.toInt())
|
||||
builder.setEntryName(entryName)
|
||||
if(VERSION.SDK_INT>=29){builder.setMmsProxyAddress(mmsProxyAddress)}
|
||||
builder.setMmsProxyPort(mmsProxyPort.toInt())
|
||||
if(VERSION.SDK_INT>=29){builder.setProxyAddress(proxyAddress)}
|
||||
builder.setProxyPort(proxyPort.toInt())
|
||||
builder.setMmsc(mmsc.toUri())
|
||||
if(VERSION.SDK_INT>=33){ builder.setMtuV4(mtuV4.toInt()); builder.setMtuV6(mtuV6.toInt()) }
|
||||
builder.setMvnoType(mvnoType)
|
||||
builder.setNetworkTypeBitmask(networkTypeBitmask.toInt())
|
||||
builder.setOperatorNumeric(operatorNumeric)
|
||||
builder.setPassword(password)
|
||||
if(VERSION.SDK_INT>=33){builder.setPersistent(persistent)}
|
||||
builder.setProtocol(protocol)
|
||||
builder.setRoamingProtocol(roamingProtocol)
|
||||
result = builder.build()
|
||||
}
|
||||
|
||||
finalStep=!finalStep
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(if(finalStep){R.string.previous_step}else{R.string.next_step}))
|
||||
}
|
||||
AnimatedVisibility(finalStep) {
|
||||
if(inputNum=="0"){
|
||||
Button(
|
||||
onClick = {myDpm.addOverrideApn(myComponent,result)},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.create))
|
||||
}
|
||||
}else{
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = {
|
||||
val success = myDpm.updateOverrideApn(myComponent,id,result)
|
||||
Toast.makeText(myContext, myContext.getString(if(success){R.string.success}else{R.string.fail}), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
Modifier.fillMaxWidth(0.49F)
|
||||
){
|
||||
Text(stringResource(R.string.update))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val success = myDpm.removeOverrideApn(myComponent,id)
|
||||
Toast.makeText(myContext, if(success){"成功"}else{"失败"}, Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
Modifier.fillMaxWidth(0.96F)
|
||||
){
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
632
app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt
Normal file
632
app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt
Normal file
@@ -0,0 +1,632 @@
|
||||
package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.KeyguardManager
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.app.admin.DevicePolicyManager.*
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build.VERSION
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
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
|
||||
import androidx.compose.material3.*
|
||||
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.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
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.Receiver
|
||||
import com.bintianqi.owndroid.ui.*
|
||||
import com.bintianqi.owndroid.ui.theme.bgColor
|
||||
|
||||
@Composable
|
||||
fun Password(navCtrl: NavHostController){
|
||||
val localNavCtrl = rememberNavController()
|
||||
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
|
||||
val scrollState = rememberScrollState()
|
||||
/*val titleMap = mapOf(
|
||||
"ResetPasswordToken" to R.string.reset_password_token,
|
||||
"PasswordInfo" to R.string.password_info,
|
||||
"ResetPassword" to R.string.reset_password,
|
||||
"RequirePasswordComplexity" to R.string.required_password_complexity,
|
||||
"KeyguardDisabledFeatures" to R.string.keyguard_disabled_features,
|
||||
"MaxTimeToLock" to R.string.max_time_to_lock,
|
||||
"PasswordTimeout" to R.string.pwd_timeout,
|
||||
"MaxPasswordFail" to R.string.max_pwd_fail,
|
||||
"PasswordHistoryLength" to R.string.pwd_history,
|
||||
"RequirePasswordQuality" to R.string.required_password_quality,
|
||||
)*/
|
||||
Scaffold(
|
||||
topBar = {
|
||||
/*TopAppBar(
|
||||
title = {Text(text = stringResource(titleMap[backStackEntry?.destination?.route]?:R.string.password_and_keyguard))},
|
||||
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.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.background(bgColor).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 = "KeyguardDisabledFeatures"){KeyguardDisabledFeatures()}
|
||||
composable(route = "MaxTimeToLock"){ScreenTimeout()}
|
||||
composable(route = "PasswordTimeout"){PasswordExpiration()}
|
||||
composable(route = "MaxPasswordFail"){MaxFailedPasswordForWipe()}
|
||||
composable(route = "PasswordHistoryLength"){PasswordHistoryLength()}
|
||||
composable(route = "RequirePasswordQuality"){PasswordQuality()}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Home(navCtrl:NavHostController,scrollState: ScrollState){
|
||||
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 = 15.dp))
|
||||
SubPageItem(R.string.password_info,"",R.drawable.info_fill0){navCtrl.navigate("PasswordInfo")}
|
||||
if(VERSION.SDK_INT>=26){
|
||||
SubPageItem(R.string.reset_password_token,"",R.drawable.key_vertical_fill0){navCtrl.navigate("ResetPasswordToken")}
|
||||
}
|
||||
SubPageItem(R.string.reset_password,"",R.drawable.lock_reset_fill0){navCtrl.navigate("ResetPassword")}
|
||||
if(VERSION.SDK_INT>=31){
|
||||
SubPageItem(R.string.required_password_complexity,"",R.drawable.password_fill0){navCtrl.navigate("RequirePasswordComplexity")}
|
||||
}
|
||||
SubPageItem(R.string.keyguard_disabled_features,"",R.drawable.screen_lock_portrait_fill0){navCtrl.navigate("KeyguardDisabledFeatures")}
|
||||
SubPageItem(R.string.max_time_to_lock,"",R.drawable.schedule_fill0){navCtrl.navigate("MaxTimeToLock")}
|
||||
SubPageItem(R.string.pwd_timeout,"",R.drawable.lock_clock_fill0){navCtrl.navigate("PasswordTimeout")}
|
||||
SubPageItem(R.string.max_pwd_fail,"",R.drawable.no_encryption_fill0){navCtrl.navigate("MaxPasswordFail")}
|
||||
SubPageItem(R.string.pwd_history,"",R.drawable.history_fill0){navCtrl.navigate("PasswordHistoryLength")}
|
||||
SubPageItem(R.string.required_password_quality,"",R.drawable.password_fill0){navCtrl.navigate("RequirePasswordQuality")}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PasswordInfo(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext, Receiver::class.java)
|
||||
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))
|
||||
if(VERSION.SDK_INT>=29){
|
||||
val passwordComplexity = mapOf(
|
||||
PASSWORD_COMPLEXITY_NONE to stringResource(R.string.password_complexity_none),
|
||||
PASSWORD_COMPLEXITY_LOW to stringResource(R.string.password_complexity_low),
|
||||
PASSWORD_COMPLEXITY_MEDIUM to stringResource(R.string.password_complexity_medium),
|
||||
PASSWORD_COMPLEXITY_HIGH to stringResource(R.string.password_complexity_high)
|
||||
)
|
||||
val pwdComplex = passwordComplexity[myDpm.passwordComplexity]
|
||||
Text(text = stringResource(R.string.current_password_complexity_is, pwdComplex?:stringResource(R.string.unknown)))
|
||||
}
|
||||
if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)){
|
||||
Text(stringResource(R.string.is_password_sufficient, myDpm.isActivePasswordSufficient))
|
||||
}
|
||||
val pwdFailedAttempts = myDpm.currentFailedPasswordAttempts
|
||||
Text(text = stringResource(R.string.password_failed_attempts_is, pwdFailedAttempts))
|
||||
if(VERSION.SDK_INT>=28&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
|
||||
val unifiedPwd = myDpm.isUsingUnifiedPassword(myComponent)
|
||||
Text(stringResource(R.string.is_using_unified_password, unifiedPwd))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun ResetPasswordToken(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val myByteArray by remember{ mutableStateOf(byteArrayOf(1,1,4,5,1,4,1,9,1,9,8,1,0,1,1,4,5,1,4,1,9,1,9,8,1,0,1,1,4,5,1,4,1,9,1,9,8,1,0)) }
|
||||
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))
|
||||
Button(
|
||||
onClick = {
|
||||
if(myDpm.clearResetPasswordToken(myComponent)){ Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
}else{ Toast.makeText(myContext, myContext.getString(R.string.fail), Toast.LENGTH_SHORT).show() }
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm)
|
||||
) {
|
||||
Text(stringResource(R.string.clear))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
try {
|
||||
if(myDpm.setResetPasswordToken(myComponent, myByteArray)){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
Toast.makeText(myContext, myContext.getString(R.string.fail), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}catch(e:SecurityException){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.security_exception), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.set))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if(!myDpm.isResetPasswordTokenActive(myComponent)){
|
||||
try{ activateToken(myContext) }
|
||||
catch(e:NullPointerException){ Toast.makeText(myContext, myContext.getString(R.string.please_set_a_token), Toast.LENGTH_SHORT).show() }
|
||||
}else{ Toast.makeText(myContext, "已经激活", Toast.LENGTH_SHORT).show() }
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.activate))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Information{Text(stringResource(R.string.activate_token_not_required_when_no_password))}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ResetPassword(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var newPwd by remember{ mutableStateOf("") }
|
||||
val myByteArray by remember{ mutableStateOf(byteArrayOf(1,1,4,5,1,4,1,9,1,9,8,1,0,1,1,4,5,1,4,1,9,1,9,8,1,0,1,1,4,5,1,4,1,9,1,9,8,1,0)) }
|
||||
var confirmed by remember{ mutableStateOf(false) }
|
||||
var resetPwdFlag 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.reset_password),style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = newPwd,
|
||||
onValueChange = {newPwd=it},
|
||||
enabled = !confirmed&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm)||myDpm.isAdminActive(myComponent)),
|
||||
label = { Text(stringResource(R.string.password))},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
Text(text = stringResource(R.string.reset_pwd_desc))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(VERSION.SDK_INT>=23){
|
||||
RadioButtonItem(
|
||||
stringResource(R.string.do_not_ask_credentials_on_boot),
|
||||
{resetPwdFlag==RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT}, {resetPwdFlag=RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT}
|
||||
)
|
||||
}
|
||||
RadioButtonItem(stringResource(R.string.reset_password_require_entry),{resetPwdFlag==RESET_PASSWORD_REQUIRE_ENTRY}, {resetPwdFlag=RESET_PASSWORD_REQUIRE_ENTRY})
|
||||
RadioButtonItem(stringResource(R.string.none),{resetPwdFlag==0},{resetPwdFlag=0})
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
if(newPwd.length>=4||newPwd.isEmpty()){ confirmed=!confirmed
|
||||
}else{ Toast.makeText(myContext, myContext.getString(R.string.require_4_digit_password), Toast.LENGTH_SHORT).show() }
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm) || isProfileOwner(myDpm) || myDpm.isAdminActive(myComponent),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = if(confirmed){ colorScheme.primary }else{ colorScheme.error },
|
||||
contentColor = if(confirmed){ colorScheme.onPrimary }else{ colorScheme.onError }
|
||||
)
|
||||
) {
|
||||
Text(text = stringResource(if(confirmed){R.string.cancel}else{R.string.confirm}))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
if(VERSION.SDK_INT>=26){
|
||||
Button(
|
||||
onClick = {
|
||||
val resetSuccess = myDpm.resetPasswordWithToken(myComponent,newPwd,myByteArray,resetPwdFlag)
|
||||
if(resetSuccess){ Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show();newPwd=""}
|
||||
else{ Toast.makeText(myContext, myContext.getString(R.string.fail), Toast.LENGTH_SHORT).show() }
|
||||
confirmed=false
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError),
|
||||
enabled = confirmed&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm)),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.reset_password_with_token))
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val resetSuccess = myDpm.resetPassword(newPwd,resetPwdFlag)
|
||||
if(resetSuccess){ Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show(); newPwd=""}
|
||||
else{ Toast.makeText(myContext, myContext.getString(R.string.fail), Toast.LENGTH_SHORT).show() }
|
||||
confirmed=false
|
||||
},
|
||||
enabled = confirmed,
|
||||
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.reset_password_deprecated))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun PasswordComplexity(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val passwordComplexity = mapOf(
|
||||
PASSWORD_COMPLEXITY_NONE to stringResource(R.string.password_complexity_none),
|
||||
PASSWORD_COMPLEXITY_LOW to stringResource(R.string.password_complexity_low),
|
||||
PASSWORD_COMPLEXITY_MEDIUM to stringResource(R.string.password_complexity_medium),
|
||||
PASSWORD_COMPLEXITY_HIGH to stringResource(R.string.password_complexity_high)
|
||||
).toList()
|
||||
var selectedItem by remember{ mutableIntStateOf(passwordComplexity[0].first) }
|
||||
if(isDeviceOwner(myDpm) || isProfileOwner(myDpm)){
|
||||
selectedItem=myDpm.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))
|
||||
RadioButtonItem(passwordComplexity[0].second,{selectedItem==passwordComplexity[0].first},{selectedItem=passwordComplexity[0].first})
|
||||
RadioButtonItem(passwordComplexity[1].second,{selectedItem==passwordComplexity[1].first},{selectedItem=passwordComplexity[1].first})
|
||||
RadioButtonItem(passwordComplexity[2].second,{selectedItem==passwordComplexity[2].first},{selectedItem=passwordComplexity[2].first})
|
||||
RadioButtonItem(passwordComplexity[3].second,{selectedItem==passwordComplexity[3].first},{selectedItem=passwordComplexity[3].first})
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.requiredPasswordComplexity = selectedItem
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)|| isProfileOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {myContext.startActivity(Intent(ACTION_SET_NEW_PASSWORD))},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.require_set_new_password))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScreenTimeout(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var inputContent by remember{ mutableStateOf(if(isDeviceOwner(myDpm)){myDpm.getMaximumTimeToLock(myComponent).toString()}else{""}) }
|
||||
var ableToApply by remember{ mutableStateOf(inputContent!="") }
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.max_time_to_lock), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text= stringResource(R.string.max_time_to_lock_desc),modifier=Modifier.padding(vertical = 2.dp))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = inputContent,
|
||||
label = { Text(stringResource(R.string.time_unit_ms))},
|
||||
onValueChange = {
|
||||
inputContent = it
|
||||
ableToApply = inputContent!=""
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = Modifier.focusable().fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {focusMgr.clearFocus() ; myDpm.setMaximumTimeToLock(myComponent,inputContent.toLong())},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MaxFailedPasswordForWipe(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var inputContent by remember{ mutableStateOf(if(isDeviceOwner(myDpm)){myDpm.getMaximumFailedPasswordsForWipe(myComponent).toString()}else{""}) }
|
||||
var ableToApply by remember{ mutableStateOf(inputContent!=""&&inputContent!="0") }
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.max_pwd_fail), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text= stringResource(R.string.max_pwd_fail_desc))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = inputContent,
|
||||
label = { Text(stringResource(R.string.max_pwd_fail_textfield))},
|
||||
onValueChange = {
|
||||
inputContent = it
|
||||
ableToApply = inputContent!=""&&inputContent!="0"
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = Modifier.focusable().fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {focusMgr.clearFocus() ; myDpm.setMaximumFailedPasswordsForWipe(myComponent,inputContent.toInt())},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PasswordExpiration(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var inputContent by remember{ mutableStateOf(if(isDeviceOwner(myDpm)){myDpm.getPasswordExpirationTimeout(myComponent).toString()}else{""}) }
|
||||
var ableToApply by remember{ mutableStateOf(inputContent!="") }
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.pwd_timeout), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text= stringResource(R.string.pwd_timeout_desc))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = inputContent,
|
||||
label = { Text(stringResource(R.string.time_unit_ms))},
|
||||
onValueChange = {
|
||||
inputContent = it
|
||||
ableToApply = inputContent!=""
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = Modifier.focusable().fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {focusMgr.clearFocus() ; myDpm.setPasswordExpirationTimeout(myComponent,inputContent.toLong())},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PasswordHistoryLength(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var inputContent by remember{ mutableStateOf(if(isDeviceOwner(myDpm)){myDpm.getPasswordHistoryLength(myComponent).toString()}else{""}) }
|
||||
var ableToApply by remember{ mutableStateOf(inputContent!="") }
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.pwd_timeout), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text= stringResource(R.string.pwd_timeout_desc))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = inputContent,
|
||||
label = { Text(stringResource(R.string.time_unit_ms))},
|
||||
onValueChange = {
|
||||
inputContent = it
|
||||
ableToApply = inputContent!=""
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = Modifier.focusable().fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {focusMgr.clearFocus() ; myDpm.setPasswordHistoryLength(myComponent,inputContent.toInt())},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun KeyguardDisabledFeatures(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
var state by remember{mutableIntStateOf(-1)}
|
||||
var shortcuts by remember{mutableStateOf(false)}
|
||||
var biometrics by remember{mutableStateOf(false)}
|
||||
var iris by remember{mutableStateOf(false)}
|
||||
var face by remember{mutableStateOf(false)}
|
||||
var remote by remember{mutableStateOf(false)}
|
||||
var fingerprint by remember{mutableStateOf(false)}
|
||||
var agents by remember{mutableStateOf(false)}
|
||||
var unredacted by remember{mutableStateOf(false)}
|
||||
var notification by remember{mutableStateOf(false)}
|
||||
var camera by remember{mutableStateOf(false)}
|
||||
var widgets by remember{mutableStateOf(false)}
|
||||
val calculateCustomFeature = {
|
||||
var calculate = myDpm.getKeyguardDisabledFeatures(myComponent)
|
||||
if(calculate==0){state=0}
|
||||
else{
|
||||
if(calculate-KEYGUARD_DISABLE_SHORTCUTS_ALL>=0 && VERSION.SDK_INT>=34){shortcuts=true;calculate-= KEYGUARD_DISABLE_SHORTCUTS_ALL }
|
||||
if(calculate-KEYGUARD_DISABLE_BIOMETRICS>=0&&VERSION.SDK_INT>=28){biometrics=true;calculate -= KEYGUARD_DISABLE_BIOMETRICS }
|
||||
if(calculate-KEYGUARD_DISABLE_IRIS>=0&&VERSION.SDK_INT>=28){iris=true;calculate -= KEYGUARD_DISABLE_IRIS }
|
||||
if(calculate-KEYGUARD_DISABLE_FACE>=0&&VERSION.SDK_INT>=28){face=true;calculate -= KEYGUARD_DISABLE_FACE }
|
||||
if(calculate-KEYGUARD_DISABLE_REMOTE_INPUT>=0&&VERSION.SDK_INT>=24){remote=true;calculate -= KEYGUARD_DISABLE_REMOTE_INPUT }
|
||||
if(calculate-KEYGUARD_DISABLE_FINGERPRINT>=0){fingerprint=true;calculate -= KEYGUARD_DISABLE_FINGERPRINT }
|
||||
if(calculate-KEYGUARD_DISABLE_TRUST_AGENTS>=0){agents=true;calculate -= KEYGUARD_DISABLE_TRUST_AGENTS }
|
||||
if(calculate-KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS>=0){unredacted=true;calculate -= KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS }
|
||||
if(calculate-KEYGUARD_DISABLE_SECURE_NOTIFICATIONS>=0){notification=true;calculate -= KEYGUARD_DISABLE_SECURE_NOTIFICATIONS }
|
||||
if(calculate-KEYGUARD_DISABLE_SECURE_CAMERA>=0){camera=true;calculate -= KEYGUARD_DISABLE_SECURE_CAMERA }
|
||||
if(calculate-KEYGUARD_DISABLE_WIDGETS_ALL>=0){widgets=true;calculate -= KEYGUARD_DISABLE_WIDGETS_ALL }
|
||||
}
|
||||
}
|
||||
if(state==-1){
|
||||
state = when(myDpm.getKeyguardDisabledFeatures(myComponent)){
|
||||
KEYGUARD_DISABLE_FEATURES_NONE->0
|
||||
KEYGUARD_DISABLE_FEATURES_ALL->1
|
||||
else->2
|
||||
}
|
||||
calculateCustomFeature()
|
||||
}
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.keyguard_disabled_features), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
RadioButtonItem(stringResource(R.string.enable_all),{state==0},{state=0})
|
||||
RadioButtonItem(stringResource(R.string.disable_all),{state==1},{state=1})
|
||||
RadioButtonItem(stringResource(R.string.custom),{state==2},{state=2})
|
||||
AnimatedVisibility(state==2) {
|
||||
Column {
|
||||
CheckBoxItem(stringResource(R.string.keyguard_disabled_features_widgets),{widgets},{widgets=!widgets})
|
||||
CheckBoxItem(stringResource(R.string.keyguard_disabled_features_camera),{camera},{camera=!camera})
|
||||
CheckBoxItem(stringResource(R.string.keyguard_disabled_features_notification),{notification},{notification=!notification})
|
||||
CheckBoxItem(stringResource(R.string.keyguard_disabled_features_unredacted_notification),{unredacted},{unredacted=!unredacted})
|
||||
CheckBoxItem(stringResource(R.string.keyguard_disabled_features_trust_agents),{agents},{agents=!agents})
|
||||
CheckBoxItem(stringResource(R.string.keyguard_disabled_features_fingerprint),{fingerprint},{fingerprint=!fingerprint})
|
||||
if(VERSION.SDK_INT>=24){ CheckBoxItem(stringResource(R.string.keyguard_disabled_features_remote_input),{remote}, {remote=!remote}) }
|
||||
if(VERSION.SDK_INT>=28){
|
||||
CheckBoxItem(stringResource(R.string.keyguard_disabled_features_face),{face},{face=!face})
|
||||
CheckBoxItem(stringResource(R.string.keyguard_disabled_features_iris),{iris},{iris=!iris})
|
||||
CheckBoxItem(stringResource(R.string.keyguard_disabled_features_biometrics),{biometrics},{biometrics=!biometrics})
|
||||
}
|
||||
if(VERSION.SDK_INT>=34){ CheckBoxItem(stringResource(R.string.keyguard_disabled_features_shortcuts),{shortcuts},{shortcuts=!shortcuts}) }
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
var result = 0
|
||||
if(state==0){ result = 0 }
|
||||
else if(state==1){ result = KEYGUARD_DISABLE_FEATURES_ALL }
|
||||
else{
|
||||
if(shortcuts&&VERSION.SDK_INT>=34){result+=KEYGUARD_DISABLE_SHORTCUTS_ALL}
|
||||
if(biometrics&&VERSION.SDK_INT>=28){result+=KEYGUARD_DISABLE_BIOMETRICS}
|
||||
if(iris&&VERSION.SDK_INT>=28){result+=KEYGUARD_DISABLE_IRIS}
|
||||
if(face&&VERSION.SDK_INT>=28){result+=KEYGUARD_DISABLE_FACE}
|
||||
if(remote&&VERSION.SDK_INT>=24){result+=KEYGUARD_DISABLE_REMOTE_INPUT}
|
||||
if(fingerprint){result+=KEYGUARD_DISABLE_FINGERPRINT}
|
||||
if(agents){result+=KEYGUARD_DISABLE_TRUST_AGENTS}
|
||||
if(unredacted){result+=KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS}
|
||||
if(notification){result+=KEYGUARD_DISABLE_SECURE_NOTIFICATIONS}
|
||||
if(camera){result+=KEYGUARD_DISABLE_SECURE_CAMERA}
|
||||
if(widgets){result+=KEYGUARD_DISABLE_WIDGETS_ALL}
|
||||
}
|
||||
myDpm.setKeyguardDisabledFeatures(myComponent,result)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
calculateCustomFeature()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PasswordQuality(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val passwordQuality = mapOf(
|
||||
PASSWORD_QUALITY_UNSPECIFIED to stringResource(R.string.password_quality_unspecified),
|
||||
PASSWORD_QUALITY_SOMETHING to stringResource(R.string.password_quality_something),
|
||||
PASSWORD_QUALITY_ALPHABETIC to stringResource(R.string.password_quality_alphabetic),
|
||||
PASSWORD_QUALITY_NUMERIC to stringResource(R.string.password_quality_numeric),
|
||||
PASSWORD_QUALITY_ALPHANUMERIC to stringResource(R.string.password_quality_alphanumeric),
|
||||
PASSWORD_QUALITY_BIOMETRIC_WEAK to stringResource(R.string.password_quality_biometrics_weak),
|
||||
PASSWORD_QUALITY_NUMERIC_COMPLEX to stringResource(R.string.password_quality_numeric_complex),
|
||||
PASSWORD_QUALITY_COMPLEX to stringResource(R.string.custom)+"(${stringResource(R.string.unsupported)})",
|
||||
).toList()
|
||||
var selectedItem by remember{ mutableIntStateOf(passwordQuality[0].first) }
|
||||
if(isDeviceOwner(myDpm) || isProfileOwner(myDpm)){ selectedItem=myDpm.getPasswordQuality(myComponent) }
|
||||
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))
|
||||
Text(text = stringResource(R.string.password_complexity_instead_password_quality))
|
||||
if(VERSION.SDK_INT>=31){ Text(text = stringResource(R.string.password_quality_deprecated_desc), color = colorScheme.error) }
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
RadioButtonItem(passwordQuality[0].second,{selectedItem==passwordQuality[0].first},{selectedItem=passwordQuality[0].first})
|
||||
RadioButtonItem(passwordQuality[1].second,{selectedItem==passwordQuality[1].first},{selectedItem=passwordQuality[1].first})
|
||||
RadioButtonItem(passwordQuality[2].second,{selectedItem==passwordQuality[2].first},{selectedItem=passwordQuality[2].first})
|
||||
RadioButtonItem(passwordQuality[3].second,{selectedItem==passwordQuality[3].first},{selectedItem=passwordQuality[3].first})
|
||||
RadioButtonItem(passwordQuality[4].second,{selectedItem==passwordQuality[4].first},{selectedItem=passwordQuality[4].first})
|
||||
RadioButtonItem(passwordQuality[5].second,{selectedItem==passwordQuality[5].first},{selectedItem=passwordQuality[5].first})
|
||||
RadioButtonItem(passwordQuality[6].second,{selectedItem==passwordQuality[6].first},{selectedItem=passwordQuality[6].first})
|
||||
RadioButtonItem(passwordQuality[7].second,{selectedItem==passwordQuality[7].first},{selectedItem=passwordQuality[7].first})
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.setPasswordQuality(myComponent,selectedItem)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm) || isProfileOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
private fun activateToken(myContext: Context){
|
||||
val desc = myContext.getString(R.string.activate_reset_password_token_here)
|
||||
val keyguardManager = myContext.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||
val confirmIntent = keyguardManager.createConfirmDeviceCredentialIntent(myContext.getString(R.string.app_name), desc)
|
||||
if (confirmIntent != null) {
|
||||
startActivity(myContext,confirmIntent, null)
|
||||
} else {
|
||||
Toast.makeText(myContext, myContext.getString(R.string.fail), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
545
app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt
Normal file
545
app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt
Normal file
@@ -0,0 +1,545 @@
|
||||
package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build.VERSION
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material3.*
|
||||
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.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
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.Receiver
|
||||
import com.bintianqi.owndroid.ui.*
|
||||
import com.bintianqi.owndroid.ui.theme.bgColor
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun DpmPermissions(navCtrl:NavHostController){
|
||||
val localNavCtrl = rememberNavController()
|
||||
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
|
||||
val scrollState = rememberScrollState()
|
||||
/*val titleMap = mapOf(
|
||||
"Home" to R.string.permission,
|
||||
"Shizuku" to R.string.shizuku,
|
||||
"DeviceAdmin" to R.string.device_admin,
|
||||
"ProfileOwner" to R.string.profile_owner,
|
||||
"DeviceOwner" to R.string.device_owner,
|
||||
"DeviceInfo" to R.string.device_info,
|
||||
"SpecificID" to R.string.enrollment_specific_id,
|
||||
"OrgName" to R.string.org_name,
|
||||
"NoManagementAccount" to R.string.account_types_management_disabled,
|
||||
"LockScreenInfo" to R.string.owner_lockscr_info,
|
||||
"SupportMsg" to R.string.support_msg,
|
||||
"TransformOwnership" to R.string.transform_ownership
|
||||
)*/
|
||||
Scaffold(
|
||||
topBar = {
|
||||
/*TopAppBar(
|
||||
title = {Text(text = stringResource(titleMap[backStackEntry?.destination?.route]?:R.string.permission))},
|
||||
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.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.background(bgColor).padding(top = it.calculateTopPadding())
|
||||
){
|
||||
composable(route = "Home"){Home(localNavCtrl,scrollState)}
|
||||
composable(route = "Shizuku"){ShizukuActivate()}
|
||||
composable(route = "DeviceAdmin"){DeviceAdmin(navCtrl)}
|
||||
composable(route = "ProfileOwner"){ProfileOwner()}
|
||||
composable(route = "DeviceOwner"){DeviceOwner(navCtrl)}
|
||||
composable(route = "DeviceInfo"){DeviceInfo()}
|
||||
composable(route = "SpecificID"){SpecificID()}
|
||||
composable(route = "OrgName"){OrgName()}
|
||||
composable(route = "NoManagementAccount"){NoManageAccount()}
|
||||
composable(route = "LockScreenInfo"){LockScreenInfo()}
|
||||
composable(route = "SupportMsg"){SupportMsg()}
|
||||
composable(route = "TransformOwnership"){TransformOwnership()}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext, Receiver::class.java)
|
||||
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 = 15.dp))
|
||||
SubPageItem(
|
||||
R.string.device_admin, stringResource(if(myDpm.isAdminActive(myComponent)){R.string.activated}else{R.string.deactivated}),
|
||||
operation = {localNavCtrl.navigate("DeviceAdmin")}
|
||||
)
|
||||
SubPageItem(
|
||||
R.string.profile_owner, stringResource(if(isProfileOwner(myDpm)){R.string.activated}else{R.string.deactivated}),
|
||||
operation = {localNavCtrl.navigate("ProfileOwner")}
|
||||
)
|
||||
SubPageItem(
|
||||
R.string.device_owner, stringResource(if(isDeviceOwner(myDpm)){R.string.activated}else{R.string.deactivated}),
|
||||
operation = {localNavCtrl.navigate("DeviceOwner")}
|
||||
)
|
||||
SubPageItem(R.string.shizuku,""){localNavCtrl.navigate("Shizuku")}
|
||||
SubPageItem(R.string.device_info,"",R.drawable.perm_device_information_fill0){localNavCtrl.navigate("DeviceInfo")}
|
||||
if(VERSION.SDK_INT>=31&&(isProfileOwner(myDpm)|| isDeviceOwner(myDpm))){
|
||||
SubPageItem(R.string.enrollment_specific_id,"",R.drawable.id_card_fill0){localNavCtrl.navigate("SpecificID")}
|
||||
}
|
||||
if((VERSION.SDK_INT>=26&&isDeviceOwner(myDpm))||(VERSION.SDK_INT>=24&&isProfileOwner(myDpm))){
|
||||
SubPageItem(R.string.org_name,"",R.drawable.corporate_fare_fill0){localNavCtrl.navigate("OrgName")}
|
||||
}
|
||||
if(isDeviceOwner(myDpm) || isProfileOwner(myDpm)){
|
||||
SubPageItem(R.string.account_types_management_disabled,"",R.drawable.account_circle_fill0){localNavCtrl.navigate("NoManagementAccount")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=24&&isDeviceOwner(myDpm)){
|
||||
SubPageItem(R.string.device_owner_lock_screen_info,"",R.drawable.screen_lock_portrait_fill0){localNavCtrl.navigate("LockScreenInfo")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=24&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
|
||||
SubPageItem(R.string.support_msg,"",R.drawable.chat_fill0){localNavCtrl.navigate("SupportMsg")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=28&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
|
||||
SubPageItem(R.string.transform_ownership,"",R.drawable.admin_panel_settings_fill0){localNavCtrl.navigate("TransformOwnership")}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun LockScreenInfo(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var infoText by remember{mutableStateOf(myDpm.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)
|
||||
OutlinedTextField(
|
||||
value = infoText,
|
||||
label = {Text(stringResource(R.string.device_owner_lock_screen_info))},
|
||||
onValueChange = { infoText=it },
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 4.dp)
|
||||
)
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
myDpm.setDeviceOwnerLockScreenInfo(myComponent,infoText)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(R.string.apply))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
myDpm.setDeviceOwnerLockScreenInfo(myComponent,null)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(R.string.reset))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DeviceAdmin(navCtrl: NavHostController){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val co = rememberCoroutineScope()
|
||||
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)
|
||||
Text(text = stringResource(if(myDpm.isAdminActive(myComponent)) { R.string.activated } else { R.string.deactivated }), style = typography.titleLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(myDpm.isAdminActive(myComponent)) {
|
||||
if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)) {
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.removeActiveAdmin(myComponent)
|
||||
co.launch{ delay(600); if(!myDpm.isAdminActive(myComponent)){navCtrl.navigateUp()} }
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError)
|
||||
) {
|
||||
Text(stringResource(R.string.deactivate))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button(onClick = {activateDeviceAdmin(myContext, myComponent)}, modifier = Modifier.fillMaxWidth()) {
|
||||
Text(stringResource(R.string.activate))
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(!myDpm.isAdminActive(myComponent)) {
|
||||
SelectionContainer {
|
||||
Text(text = stringResource(R.string.activate_device_admin_command))
|
||||
}
|
||||
CopyTextButton(myContext, R.string.copy_command, stringResource(R.string.activate_device_admin_command))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProfileOwner(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
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)
|
||||
Text(stringResource(if(isProfileOwner(myDpm)){R.string.activated}else{R.string.deactivated}), style = typography.titleLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(isProfileOwner(myDpm)&&VERSION.SDK_INT>=24){
|
||||
Button(
|
||||
onClick = {myDpm.clearProfileOwner(myComponent)},
|
||||
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError)
|
||||
) {
|
||||
Text(stringResource(R.string.deactivate))
|
||||
}
|
||||
}
|
||||
if(!isProfileOwner(myDpm)){
|
||||
SelectionContainer{
|
||||
Text(text = stringResource(R.string.activate_profile_owner_command))
|
||||
}
|
||||
CopyTextButton(myContext, R.string.copy_command, stringResource(R.string.activate_profile_owner_command))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DeviceOwner(navCtrl: NavHostController){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val co = rememberCoroutineScope()
|
||||
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)
|
||||
Text(text = stringResource(if(isDeviceOwner(myDpm)){R.string.activated}else{R.string.deactivated}), style = typography.titleLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(isDeviceOwner(myDpm)){
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.clearDeviceOwnerApp(myContext.packageName)
|
||||
co.launch{ delay(600); if(!isDeviceOwner(myDpm)){navCtrl.navigateUp()} }
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError)
|
||||
) {
|
||||
Text(text = stringResource(R.string.deactivate))
|
||||
}
|
||||
}
|
||||
if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
|
||||
SelectionContainer{
|
||||
Text(text = stringResource(R.string.activate_device_owner_command))
|
||||
}
|
||||
CopyTextButton(myContext, R.string.copy_command, stringResource(R.string.activate_device_owner_command))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DeviceInfo(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
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))
|
||||
if(VERSION.SDK_INT>=34&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
|
||||
val financed = myDpm.isDeviceFinanced
|
||||
Text(stringResource(R.string.is_device_financed, financed))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 2.dp))
|
||||
if(VERSION.SDK_INT>=33){
|
||||
val dpmRole = myDpm.devicePolicyManagementRoleHolderPackage
|
||||
Text(stringResource(R.string.dpmrh, if(dpmRole==null) { stringResource(R.string.none) } else { "" }))
|
||||
if(dpmRole!=null){
|
||||
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState())){
|
||||
Text(text = dpmRole)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 2.dp))
|
||||
val encryptionStatus = mutableMapOf(
|
||||
DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE to stringResource(R.string.es_inactive),
|
||||
DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE to stringResource(R.string.es_active),
|
||||
DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED to stringResource(R.string.es_unsupported),
|
||||
DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING to stringResource(R.string.unknown)
|
||||
)
|
||||
if(VERSION.SDK_INT>=23){ encryptionStatus[DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY] = stringResource(R.string.es_active_default_key) }
|
||||
if(VERSION.SDK_INT>=24){ encryptionStatus[DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER] = stringResource(R.string.es_active_per_user) }
|
||||
Text("加密状态:${encryptionStatus[myDpm.storageEncryptionStatus]}")
|
||||
Spacer(Modifier.padding(vertical = 2.dp))
|
||||
val adminList = myDpm.activeAdmins
|
||||
if(adminList!=null){
|
||||
var adminListText = ""
|
||||
Text(text = stringResource(R.string.activated_device_admin, adminList.size))
|
||||
var count = adminList.size
|
||||
for(each in adminList){
|
||||
count -= 1
|
||||
adminListText += "$each"
|
||||
if(count>0){adminListText += "\n"}
|
||||
}
|
||||
SelectionContainer(modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp).horizontalScroll(rememberScrollState())){
|
||||
Text(text = adminListText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun SpecificID(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)){
|
||||
val specificId = myDpm.enrollmentSpecificId
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.enrollment_specific_id), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(specificId!=""){
|
||||
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState())){ Text(specificId) }
|
||||
}else{
|
||||
Text(stringResource(R.string.require_set_org_id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun OrgName(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)){
|
||||
var orgName by remember{mutableStateOf(try{myDpm.getOrganizationName(myComponent).toString()}catch(e:SecurityException){""})}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.org_name), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = orgName, onValueChange = {orgName=it}, modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 3.dp),
|
||||
label = {Text(stringResource(R.string.org_name))},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
myDpm.setOrganizationName(myComponent,orgName)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun SupportMsg(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var shortMsg by remember{mutableStateOf(myDpm.getShortSupportMessage(myComponent)?.toString() ?: "")}
|
||||
var longMsg by remember{mutableStateOf(myDpm.getLongSupportMessage(myComponent)?.toString() ?: "")}
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.short_support_msg), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = shortMsg,
|
||||
label = {Text(stringResource(R.string.short_support_msg))},
|
||||
onValueChange = { shortMsg=it },
|
||||
modifier = Modifier.focusable().fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 2.dp))
|
||||
OutlinedTextField(
|
||||
value = longMsg,
|
||||
label = {Text(stringResource(R.string.long_support_msg))},
|
||||
onValueChange = { longMsg=it },
|
||||
modifier = Modifier.focusable().fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
myDpm.setShortSupportMessage(myComponent, shortMsg)
|
||||
myDpm.setLongSupportMessage(myComponent, longMsg)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 1.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
myDpm.setShortSupportMessage(myComponent, null)
|
||||
myDpm.setLongSupportMessage(myComponent, null)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(R.string.reset))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Information{Text(text = stringResource(R.string.support_msg_desc))}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NoManageAccount(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
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.account_types_management_disabled), style = typography.headlineLarge)
|
||||
Text(stringResource(R.string.unknown_effect))
|
||||
var accountList by remember{ mutableStateOf("") }
|
||||
val refreshList = {
|
||||
val noManageAccount = myDpm.accountTypesWithManagementDisabled
|
||||
accountList = ""
|
||||
if (noManageAccount != null) {
|
||||
var count = noManageAccount.size
|
||||
for(each in noManageAccount){ count -= 1; accountList += each; if(count>0){accountList += "\n"} }
|
||||
}
|
||||
}
|
||||
var inited by remember{mutableStateOf(false)}
|
||||
if(!inited){ refreshList(); inited=true }
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text = if(accountList==""){stringResource(R.string.none)}else{accountList})
|
||||
var inputText by remember{ mutableStateOf("") }
|
||||
OutlinedTextField(
|
||||
value = inputText,
|
||||
onValueChange = {inputText=it},
|
||||
label = {Text(stringResource(R.string.account_types))},
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 4.dp),
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
|
||||
)
|
||||
Button(
|
||||
onClick={
|
||||
myDpm.setAccountManagementDisabled(myComponent,inputText,true)
|
||||
refreshList()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
Button(
|
||||
onClick={
|
||||
myDpm.setAccountManagementDisabled(myComponent,inputText,false)
|
||||
refreshList()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun TransformOwnership(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
val focusRequester = FocusRequester()
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)){
|
||||
var pkg by remember{mutableStateOf("")}
|
||||
var cls by remember{mutableStateOf("")}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.transform_ownership), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text = stringResource(R.string.transform_ownership_desc))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = pkg, onValueChange = {pkg = it}, label = {Text(stringResource(R.string.target_package_name))},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
|
||||
keyboardActions = KeyboardActions(onNext = {focusRequester.requestFocus()})
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 2.dp))
|
||||
OutlinedTextField(
|
||||
value = cls, onValueChange = {cls = it}, label = {Text(stringResource(R.string.target_class_name))},
|
||||
modifier = Modifier.focusRequester(focusRequester).fillMaxWidth(),
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
try {
|
||||
myDpm.transferOwnership(myComponent,ComponentName(pkg, cls),null)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
}catch(e:IllegalArgumentException){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.fail), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.transform))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
private fun activateDeviceAdmin(inputContext:Context,inputComponent:ComponentName){
|
||||
try {
|
||||
val intent = Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN)
|
||||
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, inputComponent)
|
||||
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, inputContext.getString(R.string.activate_device_admin_here))
|
||||
startActivity(inputContext,intent,null)
|
||||
}catch(e:ActivityNotFoundException){
|
||||
Toast.makeText(inputContext,inputContext.getString(R.string.unsupported),Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
241
app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt
Normal file
241
app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt
Normal file
@@ -0,0 +1,241 @@
|
||||
package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Context.MODE_PRIVATE
|
||||
import android.os.Binder
|
||||
import android.os.Build.VERSION
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.SpringSpec
|
||||
import androidx.compose.foundation.focusable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.*
|
||||
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.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Warning
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.Receiver
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.*
|
||||
|
||||
@Composable
|
||||
fun ShizukuActivate(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext, Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
val filesDir = myContext.filesDir
|
||||
LaunchedEffect(Unit){ extractRish(myContext) }
|
||||
val coScope = rememberCoroutineScope()
|
||||
val scrollState = rememberScrollState()
|
||||
val outputTextScrollState = rememberScrollState()
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(scrollState),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
){
|
||||
var outputText by remember{mutableStateOf("")}
|
||||
if(Binder.getCallingUid()/100000!=0){
|
||||
Row{
|
||||
Icon(imageVector = Icons.Rounded.Warning, contentDescription = null, tint = colorScheme.onErrorContainer)
|
||||
Text(text = stringResource(R.string.not_primary_user_not_support_shizuku), color = colorScheme.onErrorContainer)
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
coScope.launch {
|
||||
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
|
||||
outputTextScrollState.animateScrollTo(0, scrollAnim())
|
||||
val getUid = executeCommand(myContext, "sh rish.sh","id -u",null,filesDir)
|
||||
outputText = if(getUid.contains("2000")){
|
||||
myContext.getString(R.string.shizuku_activated_shell)
|
||||
}else if(getUid.contains("0")){
|
||||
myContext.getString(R.string.shizuku_activated_root)
|
||||
}else if(getUid.contains("Error: 1")){
|
||||
myContext.getString(R.string.shizuku_not_started)
|
||||
}else{
|
||||
getUid
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(text = stringResource(R.string.check_shizuku))
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
coScope.launch{
|
||||
outputText= executeCommand(myContext, "sh rish.sh","dpm list-owners",null,filesDir)
|
||||
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
|
||||
outputTextScrollState.animateScrollTo(0, scrollAnim())
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(text = stringResource(R.string.list_owners))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
|
||||
if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
|
||||
Column {
|
||||
if(!myDpm.isAdminActive(myComponent)){
|
||||
Button(
|
||||
onClick = {
|
||||
coScope.launch{
|
||||
outputText = executeCommand(myContext, "sh rish.sh", myContext.getString(R.string.dpm_activate_da_command), null, filesDir)
|
||||
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
|
||||
outputTextScrollState.animateScrollTo(0, scrollAnim())
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(text = stringResource(R.string.activate_device_admin))
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
coScope.launch{
|
||||
outputText = executeCommand(myContext, "sh rish.sh", myContext.getString(R.string.dpm_activate_po_command), null, filesDir)
|
||||
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
|
||||
outputTextScrollState.animateScrollTo(0, scrollAnim())
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(text = stringResource(R.string.activate_profile_owner))
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
coScope.launch{
|
||||
outputText = executeCommand(myContext, "sh rish.sh", myContext.getString(R.string.dpm_activate_do_command), null, filesDir)
|
||||
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
|
||||
outputTextScrollState.animateScrollTo(0, scrollAnim())
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(text = stringResource(R.string.activate_device_owner))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(
|
||||
VERSION.SDK_INT>=30&&!isDeviceOwner(myDpm)&&!myDpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)&&
|
||||
!myDpm.isOrganizationOwnedDeviceWithManagedProfile
|
||||
){
|
||||
Column {
|
||||
Text(text = stringResource(R.string.org_owned_work_profile), style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
|
||||
Text(text = stringResource(R.string.input_userid_of_work_profile))
|
||||
var inputUserID by remember{mutableStateOf("")}
|
||||
OutlinedTextField(
|
||||
value = inputUserID, onValueChange = {inputUserID=it},
|
||||
label = {Text("UserID")},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.dp)
|
||||
)
|
||||
Button(
|
||||
onClick = {
|
||||
coScope.launch{
|
||||
focusMgr.clearFocus()
|
||||
outputText = executeCommand(
|
||||
myContext, "sh rish.sh", myContext.getString(R.string.activate_org_profile_command_with_user_id, inputUserID),
|
||||
null, filesDir
|
||||
)
|
||||
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
|
||||
outputTextScrollState.animateScrollTo(0, scrollAnim())
|
||||
if(myDpm.isOrganizationOwnedDeviceWithManagedProfile){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success),Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(R.string.activate))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SelectionContainer(modifier = Modifier.align(Alignment.Start).horizontalScroll(outputTextScrollState)){
|
||||
Text(text = outputText, softWrap = false, modifier = Modifier.padding(4.dp))
|
||||
}
|
||||
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
fun <T> scrollAnim(
|
||||
dampingRatio: Float = Spring.DampingRatioNoBouncy,
|
||||
stiffness: Float = Spring.StiffnessMedium,
|
||||
visibilityThreshold: T? = null
|
||||
): SpringSpec<T> = SpringSpec(dampingRatio, stiffness, visibilityThreshold)
|
||||
|
||||
fun extractRish(myContext:Context){
|
||||
val assetsMgr = myContext.assets
|
||||
myContext.deleteFile("rish.sh")
|
||||
myContext.deleteFile("rish_shizuku.dex")
|
||||
val shInput = assetsMgr.open("rish.sh")
|
||||
val shOutput = myContext.openFileOutput("rish.sh",MODE_PRIVATE)
|
||||
IOUtils.copy(shInput,shOutput)
|
||||
shOutput.close()
|
||||
val dexInput = assetsMgr.open("rish_shizuku.dex")
|
||||
val dexOutput = myContext.openFileOutput("rish_shizuku.dex",MODE_PRIVATE)
|
||||
IOUtils.copy(dexInput,dexOutput)
|
||||
dexOutput.close()
|
||||
if(VERSION.SDK_INT>=34){ Runtime.getRuntime().exec("chmod 400 rish_shizuku.dex",null,myContext.filesDir) }
|
||||
}
|
||||
|
||||
suspend fun executeCommand(myContext: Context, command: String, subCommand:String, env: Array<String>?, dir:File?): String {
|
||||
var result = ""
|
||||
val tunnel:ByteArrayInputStream
|
||||
val process:Process
|
||||
val outputStream:OutputStream
|
||||
try {
|
||||
tunnel = ByteArrayInputStream(subCommand.toByteArray())
|
||||
process = withContext(Dispatchers.IO){Runtime.getRuntime().exec(command, env, dir)}
|
||||
outputStream = process.outputStream
|
||||
IOUtils.copy(tunnel,outputStream)
|
||||
withContext(Dispatchers.IO){ outputStream.close() }
|
||||
val exitCode = withContext(Dispatchers.IO){ process.waitFor() }
|
||||
if(exitCode!=0){ result+="Error: $exitCode" }
|
||||
}catch(e:Exception){
|
||||
e.printStackTrace()
|
||||
return e.toString()
|
||||
}
|
||||
try {
|
||||
val outputReader = BufferedReader(InputStreamReader(process.inputStream))
|
||||
var outputLine: String
|
||||
while(withContext(Dispatchers.IO){ outputReader.readLine() }.also {outputLine = it}!=null) { result+="$outputLine\n" }
|
||||
val errorReader = BufferedReader(InputStreamReader(process.errorStream))
|
||||
var errorLine: String
|
||||
while(withContext(Dispatchers.IO){ errorReader.readLine() }.also {errorLine = it}!=null) { result+="$errorLine\n" }
|
||||
} catch(e: NullPointerException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
if(result==""){ return myContext.getString(R.string.try_again) }
|
||||
return result
|
||||
}
|
||||
914
app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt
Normal file
914
app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt
Normal file
@@ -0,0 +1,914 @@
|
||||
package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.app.admin.DevicePolicyManager.*
|
||||
import android.app.admin.SystemUpdateInfo
|
||||
import android.app.admin.SystemUpdatePolicy
|
||||
import android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC
|
||||
import android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_WINDOWED
|
||||
import android.app.admin.SystemUpdatePolicy.TYPE_POSTPONE
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Binder
|
||||
import android.os.Build.VERSION
|
||||
import android.os.UserManager
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material3.*
|
||||
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.stringResource
|
||||
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.Receiver
|
||||
import com.bintianqi.owndroid.toText
|
||||
import com.bintianqi.owndroid.ui.*
|
||||
import com.bintianqi.owndroid.ui.theme.bgColor
|
||||
import kotlinx.coroutines.delay
|
||||
import java.util.Date
|
||||
|
||||
@Composable
|
||||
fun SystemManage(navCtrl:NavHostController){
|
||||
val localNavCtrl = rememberNavController()
|
||||
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
|
||||
val scrollState = rememberScrollState()
|
||||
/*val titleMap = mapOf(
|
||||
"Switches" to R.string.options,
|
||||
"Keyguard" to R.string.keyguard,
|
||||
"BugReport" to R.string.request_bug_report,
|
||||
"Reboot" to R.string.reboot,
|
||||
"EditTime" to R.string.edit_time,
|
||||
"PermissionPolicy" to R.string.permission_policy,
|
||||
"MTEPolicy" to R.string.mte_policy,
|
||||
"NearbyStreamingPolicy" to R.string.nearby_streaming_policy,
|
||||
"LockTaskFeatures" to R.string.lock_task_feature,
|
||||
"CaCert" to R.string.ca_cert,
|
||||
"SecurityLogs" to R.string.security_logs,
|
||||
"SystemUpdatePolicy" to R.string.system_update_policy,
|
||||
"WipeData" to R.string.wipe_data
|
||||
)*/
|
||||
Scaffold(
|
||||
topBar = {
|
||||
/*TopAppBar(
|
||||
title = {Text(text = stringResource(titleMap[backStackEntry?.destination?.route]?:R.string.device_ctrl))},
|
||||
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.system_manage),
|
||||
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.background(bgColor).padding(top = it.calculateTopPadding())
|
||||
){
|
||||
composable(route = "Home"){Home(localNavCtrl,scrollState)}
|
||||
composable(route = "Switches"){Switches()}
|
||||
composable(route = "Keyguard"){Keyguard()}
|
||||
composable(route = "BugReport"){BugReport()}
|
||||
composable(route = "Reboot"){Reboot()}
|
||||
composable(route = "EditTime"){EditTime()}
|
||||
composable(route = "PermissionPolicy"){PermissionPolicy()}
|
||||
composable(route = "MTEPolicy"){MTEPolicy()}
|
||||
composable(route = "NearbyStreamingPolicy"){NearbyStreamingPolicy()}
|
||||
composable(route = "LockTaskFeatures"){LockTaskFeatures()}
|
||||
composable(route = "CaCert"){CaCert()}
|
||||
composable(route = "SecurityLogs"){SecurityLogs()}
|
||||
composable(route = "SystemUpdatePolicy"){SysUpdatePolicy()}
|
||||
composable(route = "WipeData"){WipeData()}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Home(navCtrl: NavHostController,scrollState: ScrollState){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)){
|
||||
Text(text = stringResource(R.string.system_manage), style = typography.headlineLarge, modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 15.dp))
|
||||
if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
|
||||
SubPageItem(R.string.options,"",R.drawable.tune_fill0){navCtrl.navigate("Switches")}
|
||||
}
|
||||
SubPageItem(R.string.keyguard,"",R.drawable.screen_lock_portrait_fill0){navCtrl.navigate("Keyguard")}
|
||||
if(VERSION.SDK_INT>=24){
|
||||
SubPageItem(R.string.request_bug_report,"",R.drawable.bug_report_fill0){navCtrl.navigate("BugReport")}
|
||||
SubPageItem(R.string.reboot,"",R.drawable.restart_alt_fill0){navCtrl.navigate("Reboot")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=28){
|
||||
SubPageItem(R.string.edit_time,"",R.drawable.schedule_fill0){navCtrl.navigate("EditTime")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=23&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
|
||||
SubPageItem(R.string.permission_policy,"",R.drawable.key_fill0){navCtrl.navigate("PermissionPolicy")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=34&&isDeviceOwner(myDpm)){
|
||||
SubPageItem(R.string.mte_policy,"",R.drawable.memory_fill0){navCtrl.navigate("MTEPolicy")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=31&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
|
||||
SubPageItem(R.string.nearby_streaming_policy,"",R.drawable.share_fill0){navCtrl.navigate("NearbyStreamingPolicy")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=28&&isDeviceOwner(myDpm)){
|
||||
SubPageItem(R.string.lock_task_feature,"",R.drawable.lock_fill0){navCtrl.navigate("LockTaskFeatures")}
|
||||
}
|
||||
if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
|
||||
SubPageItem(R.string.ca_cert,"",R.drawable.license_fill0){navCtrl.navigate("CaCert")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=26&&(isDeviceOwner(myDpm)||(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
|
||||
SubPageItem(R.string.security_logs,"",R.drawable.description_fill0){navCtrl.navigate("SecurityLogs")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=23&&isDeviceOwner(myDpm)){
|
||||
SubPageItem(R.string.system_update_policy,"",R.drawable.system_update_fill0){navCtrl.navigate("SystemUpdatePolicy")}
|
||||
}
|
||||
SubPageItem(R.string.wipe_data,"",R.drawable.warning_fill0){navCtrl.navigate("WipeData")}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
LaunchedEffect(Unit){caCert =byteArrayOf()}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Switches(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext, Receiver::class.java)
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
|
||||
SwitchItem(R.string.disable_cam,"", R.drawable.photo_camera_fill0,
|
||||
{myDpm.getCameraDisabled(null)},{myDpm.setCameraDisabled(myComponent,it)}
|
||||
)
|
||||
}
|
||||
if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
|
||||
SwitchItem(R.string.disable_screenshot, stringResource(R.string.also_disable_aosp_screen_record),R.drawable.screenshot_fill0,
|
||||
{myDpm.getScreenCaptureDisabled(null)},{myDpm.setScreenCaptureDisabled(myComponent,it) }
|
||||
)
|
||||
}
|
||||
if(VERSION.SDK_INT>=34&&(isDeviceOwner(myDpm)|| (isProfileOwner(myDpm)&&myDpm.isAffiliatedUser))){
|
||||
SwitchItem(R.string.disable_status_bar,"",R.drawable.notifications_fill0,
|
||||
{myDpm.isStatusBarDisabled},{myDpm.setStatusBarDisabled(myComponent,it) }
|
||||
)
|
||||
}
|
||||
if(isDeviceOwner(myDpm)||(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile)){
|
||||
if(VERSION.SDK_INT>=30){
|
||||
SwitchItem(R.string.auto_time,"",R.drawable.schedule_fill0,
|
||||
{myDpm.getAutoTimeEnabled(myComponent)},{myDpm.setAutoTimeEnabled(myComponent,it) }
|
||||
)
|
||||
SwitchItem(R.string.auto_timezone,"",R.drawable.globe_fill0,
|
||||
{myDpm.getAutoTimeZoneEnabled(myComponent)},{myDpm.setAutoTimeZoneEnabled(myComponent,it) }
|
||||
)
|
||||
}else{
|
||||
SwitchItem(R.string.auto_time,"",R.drawable.schedule_fill0,{myDpm.autoTimeRequired},{myDpm.setAutoTimeRequired(myComponent,it)})
|
||||
}
|
||||
}
|
||||
if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)){
|
||||
SwitchItem(R.string.master_mute,"",R.drawable.volume_up_fill0,
|
||||
{myDpm.isMasterVolumeMuted(myComponent)},{myDpm.setMasterVolumeMuted(myComponent,it) }
|
||||
)
|
||||
}
|
||||
if(VERSION.SDK_INT>=26&&(isDeviceOwner(myDpm)|| isProfileOwner(myDpm))){
|
||||
SwitchItem(R.string.backup_service,"",R.drawable.backup_fill0,
|
||||
{myDpm.isBackupServiceEnabled(myComponent)},{myDpm.setBackupServiceEnabled(myComponent,it) }
|
||||
)
|
||||
}
|
||||
if(VERSION.SDK_INT>=23&&(isDeviceOwner(myDpm)|| isProfileOwner(myDpm))){
|
||||
SwitchItem(R.string.disable_bt_contact_share,"",R.drawable.account_circle_fill0,
|
||||
{myDpm.getBluetoothContactSharingDisabled(myComponent)},{myDpm.setBluetoothContactSharingDisabled(myComponent,it)}
|
||||
)
|
||||
}
|
||||
if(VERSION.SDK_INT>=30&&isDeviceOwner(myDpm)){
|
||||
SwitchItem(R.string.common_criteria_mode, stringResource(R.string.common_criteria_mode_desc),R.drawable.security_fill0,
|
||||
{myDpm.isCommonCriteriaModeEnabled(myComponent)},{myDpm.setCommonCriteriaModeEnabled(myComponent,it)}
|
||||
)
|
||||
}
|
||||
if(VERSION.SDK_INT>=31&&(isDeviceOwner(myDpm)||(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
|
||||
SwitchItem(
|
||||
R.string.usb_signal,"",R.drawable.usb_fill0, {myDpm.isUsbDataSignalingEnabled},
|
||||
{
|
||||
if(myDpm.canUsbDataSignalingBeDisabled()){
|
||||
myDpm.isUsbDataSignalingEnabled = it
|
||||
}else{
|
||||
Toast.makeText(myContext,myContext.getString(R.string.unsupported),Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Keyguard(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.keyguard), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(VERSION.SDK_INT>=23){
|
||||
Button(
|
||||
onClick = {
|
||||
Toast.makeText(myContext,
|
||||
myContext.getString(if(myDpm.setKeyguardDisabled(myComponent,true)){R.string.success}else{R.string.fail}), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)|| (VERSION.SDK_INT>=28&&isProfileOwner(myDpm)&&myDpm.isAffiliatedUser),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.disable))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
Toast.makeText(myContext,
|
||||
myContext.getString(if(myDpm.setKeyguardDisabled(myComponent,false)){R.string.success}else{R.string.fail}), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)|| (VERSION.SDK_INT>=28&&isProfileOwner(myDpm)&&myDpm.isAffiliatedUser),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.enable))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
Information{Text(text = stringResource(R.string.require_no_password_to_disable))}
|
||||
Spacer(Modifier.padding(vertical = 8.dp))
|
||||
}
|
||||
var flag by remember{mutableIntStateOf(0)}
|
||||
Button(
|
||||
onClick = {myDpm.lockNow()},
|
||||
enabled = myDpm.isAdminActive(myComponent),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.lock_now))
|
||||
}
|
||||
if(VERSION.SDK_INT>=26){ CheckBoxItem(stringResource(R.string.require_enter_password_again),{flag==FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY},{flag = if(flag==0){1}else{0} }) }
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun BugReport(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp)){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
val result = myDpm.requestBugreport(myComponent)
|
||||
Toast.makeText(myContext, if(result){"成功"}else{"失败"}, Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
) {
|
||||
Text(stringResource(R.string.request_bug_report))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun Reboot(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp)){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Button(
|
||||
onClick = {myDpm.reboot(myComponent)},
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.reboot))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun EditTime(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
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.edit_time), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
var inputTime by remember{mutableStateOf("")}
|
||||
Text(text = stringResource(R.string.from_epoch_to_target_time))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = inputTime,
|
||||
label = { Text(stringResource(R.string.time_unit_ms))},
|
||||
onValueChange = {inputTime = it},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
enabled = isDeviceOwner(myDpm)||(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile),
|
||||
modifier = Modifier.focusable().fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {myDpm.setTime(myComponent,inputTime.toLong())},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = inputTime!=""&&(isDeviceOwner(myDpm)||(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))
|
||||
) {
|
||||
Text("应用")
|
||||
}
|
||||
Button(
|
||||
onClick = {inputTime = System.currentTimeMillis().toString()},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.get_current_time))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun PermissionPolicy(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
var selectedPolicy by remember{mutableIntStateOf(myDpm.getPermissionPolicy(myComponent))}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.permission_policy), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
RadioButtonItem(stringResource(R.string.default_stringres), {selectedPolicy==PERMISSION_POLICY_PROMPT}, {selectedPolicy= PERMISSION_POLICY_PROMPT})
|
||||
RadioButtonItem(stringResource(R.string.auto_grant), {selectedPolicy==PERMISSION_POLICY_AUTO_GRANT}, {selectedPolicy= PERMISSION_POLICY_AUTO_GRANT})
|
||||
RadioButtonItem(stringResource(R.string.auto_deny), {selectedPolicy==PERMISSION_POLICY_AUTO_DENY}, {selectedPolicy= PERMISSION_POLICY_AUTO_DENY})
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.setPermissionPolicy(myComponent,selectedPolicy)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun MTEPolicy(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
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(myDpm.mtePolicy)}
|
||||
RadioButtonItem(stringResource(R.string.decide_by_user), {selectedMtePolicy==MTE_NOT_CONTROLLED_BY_POLICY}, {selectedMtePolicy= MTE_NOT_CONTROLLED_BY_POLICY})
|
||||
RadioButtonItem(stringResource(R.string.enabled), {selectedMtePolicy==MTE_ENABLED}, {selectedMtePolicy=MTE_ENABLED})
|
||||
RadioButtonItem(stringResource(R.string.disabled), {selectedMtePolicy==MTE_DISABLED}, {selectedMtePolicy=MTE_DISABLED})
|
||||
Button(
|
||||
onClick = {
|
||||
try {
|
||||
myDpm.mtePolicy = selectedMtePolicy
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
}catch(e:java.lang.UnsupportedOperationException){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.unsupported), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
selectedMtePolicy = myDpm.mtePolicy
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Information{Text(stringResource(R.string.mte_policy_desc))}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun NearbyStreamingPolicy(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
var appPolicy by remember{mutableIntStateOf(myDpm.nearbyAppStreamingPolicy)}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.nearby_app_streaming), style = typography.titleLarge)
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
RadioButtonItem(stringResource(R.string.decide_by_user),{appPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY},{appPolicy = NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY})
|
||||
RadioButtonItem(stringResource(R.string.enabled),{appPolicy == NEARBY_STREAMING_ENABLED},{appPolicy = NEARBY_STREAMING_ENABLED})
|
||||
RadioButtonItem(stringResource(R.string.disabled),{appPolicy == NEARBY_STREAMING_DISABLED},{appPolicy = NEARBY_STREAMING_DISABLED})
|
||||
RadioButtonItem(stringResource(R.string.enable_if_secure_enough),{appPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY},{appPolicy = NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY})
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.nearbyAppStreamingPolicy = appPolicy
|
||||
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("应用")
|
||||
}
|
||||
var notificationPolicy by remember{mutableIntStateOf(myDpm.nearbyNotificationStreamingPolicy)}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.nearby_notification_streaming), style = typography.titleLarge)
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
RadioButtonItem(stringResource(R.string.decide_by_user),{notificationPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY},{notificationPolicy = NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY})
|
||||
RadioButtonItem(stringResource(R.string.enabled),{notificationPolicy == NEARBY_STREAMING_ENABLED},{notificationPolicy = NEARBY_STREAMING_ENABLED})
|
||||
RadioButtonItem(stringResource(R.string.disabled),{notificationPolicy == NEARBY_STREAMING_DISABLED},{notificationPolicy = NEARBY_STREAMING_DISABLED})
|
||||
RadioButtonItem(stringResource(R.string.enable_if_secure_enough),{notificationPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY},{notificationPolicy = NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY})
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.nearbyNotificationStreamingPolicy = notificationPolicy
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun LockTaskFeatures(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
val lockTaskPolicyList = mutableListOf(
|
||||
LOCK_TASK_FEATURE_NONE,
|
||||
LOCK_TASK_FEATURE_SYSTEM_INFO,
|
||||
LOCK_TASK_FEATURE_NOTIFICATIONS,
|
||||
LOCK_TASK_FEATURE_HOME,
|
||||
LOCK_TASK_FEATURE_OVERVIEW,
|
||||
LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
|
||||
LOCK_TASK_FEATURE_KEYGUARD
|
||||
)
|
||||
var sysInfo by remember{mutableStateOf(false)}
|
||||
var notifications by remember{mutableStateOf(false)}
|
||||
var home by remember{mutableStateOf(false)}
|
||||
var overview by remember{mutableStateOf(false)}
|
||||
var globalAction by remember{mutableStateOf(false)}
|
||||
var keyGuard by remember{mutableStateOf(false)}
|
||||
var blockAct by remember{mutableStateOf(false)}
|
||||
if(VERSION.SDK_INT>=30){lockTaskPolicyList.add(LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK)}
|
||||
var inited by remember{mutableStateOf(false)}
|
||||
var custom by remember{mutableStateOf(false)}
|
||||
val refreshFeature = {
|
||||
var calculate = myDpm.getLockTaskFeatures(myComponent)
|
||||
if(calculate!=0){
|
||||
if(VERSION.SDK_INT>=30&&calculate-lockTaskPolicyList[7]>=0){blockAct=true;calculate-=lockTaskPolicyList[7]}
|
||||
if(calculate-lockTaskPolicyList[6]>=0){keyGuard=true;calculate-=lockTaskPolicyList[6]}
|
||||
if(calculate-lockTaskPolicyList[5]>=0){globalAction=true;calculate-=lockTaskPolicyList[5]}
|
||||
if(calculate-lockTaskPolicyList[4]>=0){overview=true;calculate-=lockTaskPolicyList[4]}
|
||||
if(calculate-lockTaskPolicyList[3]>=0){home=true;calculate-=lockTaskPolicyList[3]}
|
||||
if(calculate-lockTaskPolicyList[2]>=0){notifications=true;calculate-=lockTaskPolicyList[2]}
|
||||
if(calculate-lockTaskPolicyList[1]>=0){sysInfo=true;calculate-=lockTaskPolicyList[1]}
|
||||
}else{
|
||||
custom = false
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.lock_task_feature), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(!inited){ refreshFeature();custom=myDpm.getLockTaskFeatures(myComponent)!=0;inited=true }
|
||||
RadioButtonItem(stringResource(R.string.disable_all),{!custom},{custom=false})
|
||||
RadioButtonItem(stringResource(R.string.custom),{custom},{custom=true})
|
||||
AnimatedVisibility(custom) {
|
||||
Column {
|
||||
CheckBoxItem(stringResource(R.string.ltf_sys_info),{sysInfo},{sysInfo=!sysInfo})
|
||||
CheckBoxItem(stringResource(R.string.ltf_notifications),{notifications},{notifications=!notifications})
|
||||
CheckBoxItem(stringResource(R.string.ltf_home),{home},{home=!home})
|
||||
CheckBoxItem(stringResource(R.string.ltf_overview),{overview},{overview=!overview})
|
||||
CheckBoxItem(stringResource(R.string.ltf_global_actions),{globalAction},{globalAction=!globalAction})
|
||||
CheckBoxItem(stringResource(R.string.ltf_keyguard),{keyGuard},{keyGuard=!keyGuard})
|
||||
if(VERSION.SDK_INT>=30){ CheckBoxItem(stringResource(R.string.ltf_block_activity_start_in_task),{blockAct},{blockAct=!blockAct}) }
|
||||
}
|
||||
}
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = {
|
||||
var result = lockTaskPolicyList[0]
|
||||
if(custom){
|
||||
if(blockAct&&VERSION.SDK_INT>=30){result+=lockTaskPolicyList[7]}
|
||||
if(keyGuard){result+=lockTaskPolicyList[6]}
|
||||
if(globalAction){result+=lockTaskPolicyList[5]}
|
||||
if(overview){result+=lockTaskPolicyList[4]}
|
||||
if(home){result+=lockTaskPolicyList[3]}
|
||||
if(notifications){result+=lockTaskPolicyList[2]}
|
||||
if(sysInfo){result+=lockTaskPolicyList[1]}
|
||||
}
|
||||
myDpm.setLockTaskFeatures(myComponent,result)
|
||||
refreshFeature()
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
val whitelist = myDpm.getLockTaskPackages(myComponent).toMutableList()
|
||||
var listText by remember{mutableStateOf("")}
|
||||
var inputPkg by remember{mutableStateOf("")}
|
||||
val refreshWhitelist = {
|
||||
inputPkg=""
|
||||
listText=""
|
||||
listText = whitelist.toText()
|
||||
}
|
||||
LaunchedEffect(Unit){refreshWhitelist()}
|
||||
Text(text = stringResource(R.string.whitelist_app), style = typography.titleLarge)
|
||||
SelectionContainer(modifier = Modifier.animateContentSize(Animations().animateListSize)){
|
||||
Text(text = if(listText==""){ stringResource(R.string.none) }else{listText})
|
||||
}
|
||||
OutlinedTextField(
|
||||
value = inputPkg,
|
||||
onValueChange = {inputPkg=it},
|
||||
label = {Text(stringResource(R.string.package_name))},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 3.dp)
|
||||
)
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
whitelist.add(inputPkg)
|
||||
myDpm.setLockTaskPackages(myComponent,whitelist.toTypedArray())
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
refreshWhitelist()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
if(inputPkg in whitelist){
|
||||
whitelist.remove(inputPkg)
|
||||
myDpm.setLockTaskPackages(myComponent,whitelist.toTypedArray())
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
Toast.makeText(myContext, myContext.getString(R.string.not_exist), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
refreshWhitelist()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CaCert(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
var exist by remember{mutableStateOf(false)}
|
||||
var isEmpty by remember{mutableStateOf(true)}
|
||||
val refresh = {
|
||||
isEmpty = caCert.isEmpty()
|
||||
exist = if(!isEmpty){ myDpm.hasCaCertInstalled(myComponent, caCert) }else{ false }
|
||||
}
|
||||
LaunchedEffect(exist){ while(true){ refresh();delay(600) } }
|
||||
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))
|
||||
Text(text = if(isEmpty){stringResource(R.string.please_select_ca_cert)}else{stringResource(R.string.cacert_installed, exist)}, modifier = Modifier.animateContentSize())
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
val caCertIntent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
caCertIntent.setType("*/*")
|
||||
caCertIntent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
getCaCert.launch(caCertIntent)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.select_ca_cert))
|
||||
}
|
||||
AnimatedVisibility(!isEmpty) {
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = {
|
||||
val result = myDpm.installCaCert(myComponent, caCert)
|
||||
Toast.makeText(myContext, myContext.getString(if(result){R.string.success}else{R.string.fail}), Toast.LENGTH_SHORT).show()
|
||||
refresh()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.install))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if(exist){
|
||||
myDpm.uninstallCaCert(myComponent, caCert)
|
||||
exist = myDpm.hasCaCertInstalled(myComponent, caCert)
|
||||
Toast.makeText(myContext, myContext.getString(if(exist){R.string.fail}else{R.string.success}), Toast.LENGTH_SHORT).show()
|
||||
}else{ Toast.makeText(myContext, myContext.getString(R.string.not_exist), Toast.LENGTH_SHORT).show() }
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.uninstall))
|
||||
}
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.uninstallAllUserCaCerts(myComponent)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.uninstall_all_user_ca_cert))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun SecurityLogs(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
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))
|
||||
Text(text = stringResource(R.string.developing))
|
||||
SwitchItem(R.string.enable,"",null,{myDpm.isSecurityLoggingEnabled(myComponent)},{myDpm.setSecurityLoggingEnabled(myComponent,it)})
|
||||
Button(
|
||||
onClick = {
|
||||
val log = myDpm.retrieveSecurityLogs(myComponent)
|
||||
if(log!=null){
|
||||
for(i in log){ Log.d("SecureLog",i.toString()) }
|
||||
Toast.makeText(myContext,myContext.getString(R.string.success),Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
Log.d("SecureLog",myContext.getString(R.string.none))
|
||||
Toast.makeText(myContext, myContext.getString(R.string.no_logs),Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.security_logs))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val log = myDpm.retrievePreRebootSecurityLogs(myComponent)
|
||||
if(log!=null){
|
||||
for(i in log){ Log.d("SecureLog",i.toString()) }
|
||||
Toast.makeText(myContext,myContext.getString(R.string.success),Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
Log.d("SecureLog",myContext.getString(R.string.none))
|
||||
Toast.makeText(myContext,myContext.getString(R.string.no_logs),Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.pre_reboot_security_logs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WipeData(){
|
||||
val myContext = LocalContext.current
|
||||
val userManager = myContext.getSystemService(Context.USER_SERVICE) as UserManager
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
var flag by remember{ mutableIntStateOf(0) }
|
||||
var confirmed by remember{ mutableStateOf(false) }
|
||||
var externalStorage by remember{mutableStateOf(false)}
|
||||
var protectionData by remember{mutableStateOf(false)}
|
||||
var euicc by remember{mutableStateOf(false)}
|
||||
var silent by remember{mutableStateOf(false)}
|
||||
var reason by remember{mutableStateOf("")}
|
||||
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))
|
||||
CheckBoxItem(stringResource(R.string.wipe_external_storage),{externalStorage},{externalStorage=!externalStorage;confirmed=false})
|
||||
if(VERSION.SDK_INT>=22&&isDeviceOwner(myDpm)){
|
||||
CheckBoxItem(stringResource(R.string.wipe_reset_protection_data),{protectionData},{protectionData=!protectionData;confirmed=false})
|
||||
}
|
||||
if(VERSION.SDK_INT>=28){ CheckBoxItem(stringResource(R.string.wipe_euicc),{euicc},{euicc=!euicc;confirmed=false}) }
|
||||
if(VERSION.SDK_INT>=29){ CheckBoxItem(stringResource(R.string.wipe_silently),{silent},{silent=!silent;confirmed=false}) }
|
||||
AnimatedVisibility(!silent&&VERSION.SDK_INT>=28) {
|
||||
OutlinedTextField(
|
||||
value = reason, onValueChange = {reason=it},
|
||||
label = {Text(stringResource(R.string.reason))},
|
||||
enabled = !confirmed,
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 3.dp)
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
flag = 0
|
||||
if(externalStorage){flag += WIPE_EXTERNAL_STORAGE}
|
||||
if(protectionData&&VERSION.SDK_INT>=22){flag += WIPE_RESET_PROTECTION_DATA}
|
||||
if(euicc&&VERSION.SDK_INT>=28){flag += WIPE_EUICC}
|
||||
if(reason==""){silent = true}
|
||||
if(silent&&VERSION.SDK_INT>=29){flag += WIPE_SILENTLY}
|
||||
confirmed=!confirmed
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = if(confirmed){ colorScheme.primary }else{ colorScheme.error },
|
||||
contentColor = if(confirmed){ colorScheme.onPrimary }else{ colorScheme.onError }
|
||||
),
|
||||
enabled = myDpm.isAdminActive(myComponent),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(if(confirmed){ R.string.cancel }else{ R.string.confirm }))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if(VERSION.SDK_INT>=28){myDpm.wipeData(flag,reason)}
|
||||
else{myDpm.wipeData(flag)}
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError),
|
||||
enabled = confirmed&&(VERSION.SDK_INT<34||(VERSION.SDK_INT>=34&&!userManager.isSystemUser)),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("WipeData")
|
||||
}
|
||||
if (VERSION.SDK_INT >= 34&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))) {
|
||||
Button(
|
||||
onClick = {myDpm.wipeDevice(flag)},
|
||||
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError),
|
||||
enabled = confirmed,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("WipeDevice")
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(VERSION.SDK_INT>=24&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
|
||||
Information{Text(text = stringResource(R.string.will_delete_work_profile))}
|
||||
}
|
||||
if(VERSION.SDK_INT>=34&&Binder.getCallingUid()/100000==0){
|
||||
Information{Text(text = stringResource(R.string.api34_or_above_wipedata_cannot_in_system_user))}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SysUpdatePolicy(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
val sharedPref = myContext.getSharedPreferences("data", Context.MODE_PRIVATE)
|
||||
val isWear = sharedPref.getBoolean("isWear",false)
|
||||
val bodyTextStyle = if(isWear){ typography.bodyMedium}else{typography.bodyLarge}
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
if(VERSION.SDK_INT>=23){
|
||||
Column {
|
||||
var selectedPolicy by remember{ mutableStateOf(myDpm.systemUpdatePolicy?.policyType) }
|
||||
Text(text = stringResource(R.string.system_update_policy), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
RadioButtonItem(stringResource(R.string.system_update_policy_automatic),{selectedPolicy==TYPE_INSTALL_AUTOMATIC},{selectedPolicy= TYPE_INSTALL_AUTOMATIC})
|
||||
RadioButtonItem(stringResource(R.string.system_update_policy_install_windowed),{selectedPolicy==TYPE_INSTALL_WINDOWED},{selectedPolicy= TYPE_INSTALL_WINDOWED})
|
||||
RadioButtonItem(stringResource(R.string.system_update_policy_postpone),{selectedPolicy==TYPE_POSTPONE},{selectedPolicy= TYPE_POSTPONE})
|
||||
RadioButtonItem(stringResource(R.string.none),{selectedPolicy == null},{selectedPolicy=null})
|
||||
var windowedPolicyStart by remember{ mutableStateOf("") }
|
||||
var windowedPolicyEnd by remember{ mutableStateOf("") }
|
||||
if(selectedPolicy==2){
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
OutlinedTextField(
|
||||
value = windowedPolicyStart,
|
||||
label = { Text(stringResource(R.string.start_time))},
|
||||
onValueChange = {windowedPolicyStart=it},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth(0.5F)
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 3.dp))
|
||||
OutlinedTextField(
|
||||
value = windowedPolicyEnd,
|
||||
onValueChange = {windowedPolicyEnd=it},
|
||||
label = {Text(stringResource(R.string.end_time))},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
Text(text = stringResource(R.string.minutes_in_one_day), style = bodyTextStyle)
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val policy =
|
||||
when(selectedPolicy){
|
||||
TYPE_INSTALL_AUTOMATIC-> SystemUpdatePolicy.createAutomaticInstallPolicy()
|
||||
TYPE_INSTALL_WINDOWED-> SystemUpdatePolicy.createWindowedInstallPolicy(windowedPolicyStart.toInt(),windowedPolicyEnd.toInt())
|
||||
TYPE_POSTPONE-> SystemUpdatePolicy.createPostponeInstallPolicy()
|
||||
else->null
|
||||
}
|
||||
myDpm.setSystemUpdatePolicy(myComponent,policy)
|
||||
Toast.makeText(myContext, "成功!", Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
}
|
||||
}
|
||||
if(VERSION.SDK_INT>=26){
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
val sysUpdateInfo = myDpm.getPendingSystemUpdate(myComponent)
|
||||
Column {
|
||||
if(sysUpdateInfo!=null){
|
||||
Text(text = stringResource(R.string.update_received_time, Date(sysUpdateInfo.receivedTime)), style = bodyTextStyle)
|
||||
val securityStateDesc = when(sysUpdateInfo.securityPatchState){
|
||||
SystemUpdateInfo.SECURITY_PATCH_STATE_UNKNOWN-> stringResource(R.string.unknown)
|
||||
SystemUpdateInfo.SECURITY_PATCH_STATE_TRUE->"true"
|
||||
else->"false"
|
||||
}
|
||||
Text(text = stringResource(R.string.is_security_patch, securityStateDesc), style = bodyTextStyle)
|
||||
}else{
|
||||
Text(text = stringResource(R.string.no_system_update), style = bodyTextStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
/*if(VERSION.SDK_INT>=29){
|
||||
Column(modifier = sections()){
|
||||
var resultUri by remember{mutableStateOf(otaUri)}
|
||||
Text(text = "安装系统更新", style = typography.titleLarge)
|
||||
Button(
|
||||
onClick = {
|
||||
val getUri = Intent(Intent.ACTION_GET_CONTENT)
|
||||
getUri.setType("application/zip")
|
||||
getUri.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
getOtaPackage.launch(getUri)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm)
|
||||
) {
|
||||
Text("选择OTA包")
|
||||
}
|
||||
Button(
|
||||
onClick = {resultUri = otaUri},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm)
|
||||
) {
|
||||
Text("查看OTA包详情")
|
||||
}
|
||||
Text("URI: $resultUri")
|
||||
if(installOta){
|
||||
Button(
|
||||
onClick = {
|
||||
val sysUpdateExecutor = Executors.newCachedThreadPool()
|
||||
val sysUpdateCallback:InstallSystemUpdateCallback = InstallSystemUpdateCallback
|
||||
myDpm.installSystemUpdate(myComponent,resultUri,sysUpdateExecutor,sysUpdateCallback)
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm)
|
||||
){
|
||||
Text("安装")
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
547
app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt
Normal file
547
app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt
Normal file
@@ -0,0 +1,547 @@
|
||||
package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.Binder
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Process
|
||||
import android.os.UserHandle
|
||||
import android.os.UserManager
|
||||
import android.provider.MediaStore
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
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
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
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
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.os.UserManagerCompat
|
||||
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.Receiver
|
||||
import com.bintianqi.owndroid.toText
|
||||
import com.bintianqi.owndroid.ui.*
|
||||
import com.bintianqi.owndroid.ui.theme.bgColor
|
||||
import com.bintianqi.owndroid.uriToStream
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
var affiliationID = mutableSetOf<String>()
|
||||
@Composable
|
||||
fun UserManage(navCtrl:NavHostController) {
|
||||
val localNavCtrl = rememberNavController()
|
||||
val backStackEntry by localNavCtrl.currentBackStackEntryAsState()
|
||||
val scrollState = rememberScrollState()
|
||||
/*val titleMap = mapOf(
|
||||
"UserInfo" to R.string.user_info,
|
||||
"UserOperation" to R.string.user_operation,
|
||||
"CreateUser" to R.string.create_user,
|
||||
"EditUsername" to R.string.edit_username,
|
||||
"ChangeUserIcon" to R.string.change_user_icon,
|
||||
"UserSessionMessage" to R.string.user_session_msg,
|
||||
"AffiliationID" to R.string.affiliation_id,
|
||||
)*/
|
||||
Scaffold(
|
||||
topBar = {
|
||||
/*TopAppBar(
|
||||
title = {Text(text = stringResource(titleMap[backStackEntry?.destination?.route]?:R.string.user_manage))},
|
||||
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_manager),
|
||||
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.background(bgColor).padding(top = it.calculateTopPadding())
|
||||
){
|
||||
composable(route = "Home"){Home(localNavCtrl,scrollState)}
|
||||
composable(route = "UserInfo"){CurrentUserInfo()}
|
||||
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){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)){
|
||||
Text(text = stringResource(R.string.user_manager), style = typography.headlineLarge, modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 15.dp))
|
||||
SubPageItem(R.string.user_info,"",R.drawable.person_fill0){navCtrl.navigate("UserInfo")}
|
||||
SubPageItem(R.string.user_operation,"",R.drawable.sync_alt_fill0){navCtrl.navigate("UserOperation")}
|
||||
if(VERSION.SDK_INT>=24&&isDeviceOwner(myDpm)){
|
||||
SubPageItem(R.string.create_user,"",R.drawable.person_add_fill0){navCtrl.navigate("CreateUser")}
|
||||
}
|
||||
SubPageItem(R.string.edit_username,"",R.drawable.edit_fill0){navCtrl.navigate("EditUsername")}
|
||||
if(VERSION.SDK_INT>=23&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
|
||||
SubPageItem(R.string.change_user_icon,"",R.drawable.account_circle_fill0){navCtrl.navigate("ChangeUserIcon")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=28&&isDeviceOwner(myDpm)){
|
||||
SubPageItem(R.string.user_session_msg,"",R.drawable.notifications_fill0){navCtrl.navigate("UserSessionMessage")}
|
||||
}
|
||||
if(VERSION.SDK_INT>=26&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
|
||||
SubPageItem(R.string.affiliation_id,"",R.drawable.id_card_fill0){navCtrl.navigate("AffiliationID")}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CurrentUserInfo(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext, Receiver::class.java)
|
||||
val userManager = myContext.getSystemService(Context.USER_SERVICE) as UserManager
|
||||
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))
|
||||
Text(stringResource(R.string.is_user_unlocked, UserManagerCompat.isUserUnlocked(myContext)))
|
||||
if(VERSION.SDK_INT>=24){ Text(stringResource(R.string.is_support_multi_user, UserManager.supportsMultipleUsers())) }
|
||||
if(VERSION.SDK_INT>=23){ Text(text = stringResource(R.string.is_system_user, userManager.isSystemUser)) }
|
||||
if(VERSION.SDK_INT>=34){ Text(text = stringResource(R.string.is_admin_user, userManager.isAdminUser)) }
|
||||
if(VERSION.SDK_INT>=31){ Text(text = stringResource(R.string.is_headless_system_user, UserManager.isHeadlessSystemUserMode())) }
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if (VERSION.SDK_INT >= 28) {
|
||||
val logoutable = myDpm.isLogoutEnabled
|
||||
Text(text = stringResource(R.string.user_can_logout, logoutable))
|
||||
if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)){
|
||||
val ephemeralUser = myDpm.isEphemeralUser(myComponent)
|
||||
Text(text = stringResource(R.string.is_ephemeral_user, ephemeralUser))
|
||||
}
|
||||
Text(text = stringResource(R.string.is_affiliated_user, myDpm.isAffiliatedUser))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text = stringResource(R.string.user_id_is, Binder.getCallingUid()/100000))
|
||||
Text(text = stringResource(R.string.user_serial_number_is, userManager.getSerialNumberForUser(Process.myUserHandle())))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UserOperation(){
|
||||
val myContext = LocalContext.current
|
||||
val userManager = myContext.getSystemService(Context.USER_SERVICE) as UserManager
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
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.user_operation), style = typography.headlineLarge)
|
||||
var idInput by remember{ mutableStateOf("") }
|
||||
var userHandleById:UserHandle by remember{ mutableStateOf(Process.myUserHandle()) }
|
||||
var useUid by remember{ mutableStateOf(false) }
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = idInput,
|
||||
onValueChange = {
|
||||
idInput=it
|
||||
if(useUid){
|
||||
if(idInput!=""&&VERSION.SDK_INT>=24){
|
||||
userHandleById = UserHandle.getUserHandleForUid(idInput.toInt())
|
||||
}
|
||||
}else{
|
||||
val userHandleBySerial = userManager.getUserForSerialNumber(idInput.toLong())
|
||||
userHandleById = userHandleBySerial ?: Process.myUserHandle()
|
||||
}
|
||||
},
|
||||
label = {Text(if(useUid){"UID"}else{ stringResource(R.string.serial_number) })},
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = Modifier.focusable().fillMaxWidth(),
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
if(VERSION.SDK_INT>=24){
|
||||
CheckBoxItem(text = stringResource(R.string.use_uid), checked = {useUid}, operation = {idInput=""; useUid = !useUid})
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(VERSION.SDK_INT>28){
|
||||
if(isProfileOwner(myDpm)&&myDpm.isAffiliatedUser){
|
||||
Button(
|
||||
onClick = {
|
||||
val result = myDpm.logoutUser(myComponent)
|
||||
Toast.makeText(myContext, userOperationResultCode(result,myContext), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = isProfileOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.logout_current_user))
|
||||
}
|
||||
}
|
||||
}
|
||||
if(VERSION.SDK_INT>=28){
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
val result = myDpm.startUserInBackground(myComponent,userHandleById)
|
||||
Toast.makeText(myContext, userOperationResultCode(result,myContext), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text(stringResource(R.string.start_in_background))
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
Toast.makeText(
|
||||
myContext,
|
||||
myContext.getString(if(myDpm.switchUser(myComponent,userHandleById)) { R.string.success }else{ R.string.fail }), Toast.LENGTH_SHORT
|
||||
).show()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.user_operation_switch))
|
||||
}
|
||||
if(VERSION.SDK_INT>=28){
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
try{
|
||||
val result = myDpm.stopUser(myComponent,userHandleById)
|
||||
Toast.makeText(myContext, userOperationResultCode(result,myContext), Toast.LENGTH_SHORT).show()
|
||||
}catch(e:IllegalArgumentException){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.fail), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.user_operation_stop))
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
if(myDpm.removeUser(myComponent,userHandleById)){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
idInput=""
|
||||
}else{
|
||||
Toast.makeText(myContext, myContext.getString(R.string.fail), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.user_operation_remove))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun CreateUser(){
|
||||
val myContext = LocalContext.current
|
||||
val userManager = myContext.getSystemService(Context.USER_SERVICE) as UserManager
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var userName by remember{ mutableStateOf("") }
|
||||
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))
|
||||
OutlinedTextField(
|
||||
value = userName,
|
||||
onValueChange = {userName=it},
|
||||
label = {Text(stringResource(R.string.username))},
|
||||
modifier = Modifier.focusable().fillMaxWidth(),
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
var selectedFlag by remember{ mutableIntStateOf(0) }
|
||||
RadioButtonItem(stringResource(R.string.none),{selectedFlag==0},{selectedFlag=0})
|
||||
RadioButtonItem(stringResource(R.string.create_user_skip_wizard),{selectedFlag==DevicePolicyManager.SKIP_SETUP_WIZARD},{selectedFlag=DevicePolicyManager.SKIP_SETUP_WIZARD})
|
||||
if(VERSION.SDK_INT>=28){
|
||||
RadioButtonItem(stringResource(R.string.create_user_ephemeral_user),{selectedFlag==DevicePolicyManager.MAKE_USER_EPHEMERAL},{selectedFlag=DevicePolicyManager.MAKE_USER_EPHEMERAL})
|
||||
RadioButtonItem(stringResource(R.string.create_user_enable_all_system_app),{selectedFlag==DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED},{selectedFlag=DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED})
|
||||
}
|
||||
var newUserHandle: UserHandle? by remember{ mutableStateOf(null) }
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
newUserHandle=myDpm.createAndManageUser(myComponent,userName,myComponent,null,selectedFlag)
|
||||
focusMgr.clearFocus()
|
||||
Toast.makeText(myContext, if(newUserHandle!=null){"成功"}else{"失败"}, Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.create))
|
||||
}
|
||||
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(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var input by remember{mutableStateOf("")}
|
||||
var list by remember{mutableStateOf("")}
|
||||
LaunchedEffect(Unit){
|
||||
affiliationID = myDpm.getAffiliationIds(myComponent)
|
||||
list = affiliationID.toText()
|
||||
}
|
||||
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))
|
||||
if(list!=""){
|
||||
SelectionContainer {
|
||||
Text(text = list)
|
||||
}
|
||||
}else{
|
||||
Text(text = stringResource(R.string.none))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = input,
|
||||
onValueChange = {input = it},
|
||||
label = {Text("ID")},
|
||||
modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.dp),
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||
Button(
|
||||
onClick = { affiliationID.add(input); list = affiliationID.toText() },
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
){
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
Button(
|
||||
onClick = { affiliationID.remove(input); list = affiliationID.toText() },
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
){
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if("" in affiliationID) {
|
||||
Toast.makeText(myContext, myContext.getString(R.string.include_empty_string), Toast.LENGTH_SHORT).show()
|
||||
}else if(affiliationID.isEmpty()){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.cannot_be_empty), Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
myDpm.setAffiliationIds(myComponent, affiliationID)
|
||||
affiliationID = myDpm.getAffiliationIds(myComponent)
|
||||
list = affiliationID.toText()
|
||||
Toast.makeText(myContext,myContext.getString(R.string.success),Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Username(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
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.edit_username), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
OutlinedTextField(
|
||||
value = inputUsername,
|
||||
onValueChange = {inputUsername=it},
|
||||
label = {Text(stringResource(R.string.username))},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth(),
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm)
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.setProfileName(myComponent,inputUsername)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.setProfileName(myComponent,null)
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.reset))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun UserSessionMessage(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
val getStart = myDpm.getStartUserSessionMessage(myComponent)?:""
|
||||
val getEnd = myDpm.getEndUserSessionMessage(myComponent)?:""
|
||||
var start by remember{mutableStateOf(getStart.toString())}
|
||||
var end by remember{mutableStateOf(getEnd.toString())}
|
||||
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))
|
||||
OutlinedTextField(
|
||||
value = start,
|
||||
onValueChange = {start=it},
|
||||
label = {Text(stringResource(R.string.start_user_session_msg))},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth(),
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm)
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 2.dp))
|
||||
OutlinedTextField(
|
||||
value = end,
|
||||
onValueChange = {end=it},
|
||||
label = {Text(stringResource(R.string.end_user_session_msg))},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.focusable().fillMaxWidth(),
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm)
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.setStartUserSessionMessage(myComponent,start)
|
||||
myDpm.setEndUserSessionMessage(myComponent,end)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
myDpm.setStartUserSessionMessage(myComponent,null)
|
||||
myDpm.setEndUserSessionMessage(myComponent,null)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.reset))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun UserIcon(){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
var getContent by remember{mutableStateOf(false)}
|
||||
var canApply 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.change_user_icon), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text(text = stringResource(R.string.pick_a_square_image))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
CheckBoxItem(stringResource(R.string.file_picker_instead_gallery),{getContent},{getContent=!getContent})
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(if(getContent){Intent.ACTION_GET_CONTENT}else{Intent.ACTION_PICK})
|
||||
if(getContent){intent.addCategory(Intent.CATEGORY_OPENABLE)}
|
||||
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
|
||||
getUserIcon.launch(intent)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.select_picture))
|
||||
}
|
||||
LaunchedEffect(Unit){ delay(600); canApply = userIconUri!=null }
|
||||
AnimatedVisibility(canApply) {
|
||||
Button(
|
||||
onClick = {
|
||||
uriToStream(myContext, userIconUri){stream ->
|
||||
val bitmap = BitmapFactory.decodeStream(stream)
|
||||
myDpm.setUserIcon(myComponent,bitmap)
|
||||
Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun userOperationResultCode(result:Int, myContext: Context): String {
|
||||
return when(result){
|
||||
UserManager.USER_OPERATION_SUCCESS->myContext.getString(R.string.success)
|
||||
UserManager.USER_OPERATION_ERROR_UNKNOWN-> myContext.getString(R.string.unknown_result)
|
||||
UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE-> myContext.getString(R.string.fail_managed_profile)
|
||||
UserManager.USER_OPERATION_ERROR_CURRENT_USER-> myContext.getString(R.string.fail_current_user)
|
||||
else->"未知"
|
||||
}
|
||||
}
|
||||
319
app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt
Normal file
319
app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt
Normal file
@@ -0,0 +1,319 @@
|
||||
package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.os.Build.VERSION
|
||||
import android.os.UserManager
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.background
|
||||
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.Receiver
|
||||
import com.bintianqi.owndroid.ui.Animations
|
||||
import com.bintianqi.owndroid.ui.SubPageItem
|
||||
import com.bintianqi.owndroid.ui.SwitchItem
|
||||
import com.bintianqi.owndroid.ui.TopBar
|
||||
import com.bintianqi.owndroid.ui.theme.bgColor
|
||||
|
||||
private data class Restriction(
|
||||
val restriction:String,
|
||||
@StringRes val name:Int,
|
||||
val desc:String,
|
||||
@DrawableRes val ico:Int
|
||||
)
|
||||
|
||||
@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.background(bgColor).padding(top = it.calculateTopPadding())
|
||||
){
|
||||
composable(route = "Internet"){Internet()}
|
||||
composable(route = "Home"){Home(localNavCtrl,scrollState)}
|
||||
composable(route = "Connectivity"){Connectivity()}
|
||||
composable(route = "Applications"){Application()}
|
||||
composable(route = "Users"){User()}
|
||||
composable(route = "Media"){Media()}
|
||||
composable(route = "Other"){Other()}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Home(navCtrl:NavHostController,scrollState: ScrollState){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext, Receiver::class.java)
|
||||
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 = 15.dp))
|
||||
Text(text = stringResource(R.string.switch_to_disable_feature), modifier = Modifier.padding(start = 15.dp))
|
||||
if(isProfileOwner(myDpm)){ Text(text = stringResource(R.string.profile_owner_is_restricted), modifier = Modifier.padding(start = 15.dp)) }
|
||||
if(isProfileOwner(myDpm)&&(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isManagedProfile(myComponent)))){
|
||||
Text(text = stringResource(R.string.some_features_invalid_in_work_profile), modifier = Modifier.padding(start = 15.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))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun Internet(){
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())){
|
||||
for(internetItem in RestrictionData().internet()){
|
||||
UserRestrictionItem(internetItem.restriction,internetItem.name,internetItem.desc,internetItem.ico)
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Connectivity(){
|
||||
val myContext = LocalContext.current
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())){
|
||||
for(connectivityItem in RestrictionData().connectivity(myContext)){
|
||||
UserRestrictionItem(connectivityItem.restriction,connectivityItem.name,connectivityItem.desc,connectivityItem.ico)
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Application(){
|
||||
val myContext = LocalContext.current
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())){
|
||||
for(applicationItem in RestrictionData().application(myContext)){
|
||||
UserRestrictionItem(applicationItem.restriction,applicationItem.name,applicationItem.desc,applicationItem.ico)
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun User(){
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())){
|
||||
for(userItem in RestrictionData().user()){
|
||||
UserRestrictionItem(userItem.restriction,userItem.name,userItem.desc,userItem.ico)
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Media(){
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())){
|
||||
for(mediaItem in RestrictionData().media()){
|
||||
UserRestrictionItem(mediaItem.restriction,mediaItem.name,mediaItem.desc,mediaItem.ico)
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Other(){
|
||||
val myContext = LocalContext.current
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())){
|
||||
for(otherItem in RestrictionData().other(myContext)){
|
||||
UserRestrictionItem(otherItem.restriction,otherItem.name,otherItem.desc,otherItem.ico)
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
private fun UserRestrictionItem(
|
||||
restriction:String, itemName:Int,
|
||||
restrictionDescription:String,
|
||||
leadIcon:Int
|
||||
){
|
||||
val myContext = LocalContext.current
|
||||
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
val myComponent = ComponentName(myContext,Receiver::class.java)
|
||||
SwitchItem(
|
||||
itemName,restrictionDescription,leadIcon,
|
||||
{ if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){ myDpm.getUserRestrictions(myComponent).getBoolean(restriction) }else{ false } },
|
||||
{
|
||||
try{
|
||||
if(it){
|
||||
myDpm.addUserRestriction(myComponent,restriction)
|
||||
}else{
|
||||
myDpm.clearUserRestriction(myComponent,restriction)
|
||||
}
|
||||
}catch(e:SecurityException){
|
||||
if(isProfileOwner(myDpm)){
|
||||
Toast.makeText(myContext, myContext.getString(R.string.require_device_owner), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
},
|
||||
isDeviceOwner(myDpm)||isProfileOwner(myDpm)
|
||||
)
|
||||
}
|
||||
|
||||
private class RestrictionData{
|
||||
fun internet():List<Restriction>{
|
||||
val list:MutableList<Restriction> = mutableListOf()
|
||||
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)
|
||||
if(VERSION.SDK_INT>=24){list += Restriction(UserManager.DISALLOW_DATA_ROAMING,R.string.data_roaming,"",R.drawable.network_cell_fill0)}
|
||||
if(VERSION.SDK_INT>=34){
|
||||
list += Restriction(UserManager.DISALLOW_CELLULAR_2G,R.string.cellular_2g,"",R.drawable.network_cell_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO,R.string.ultra_wideband_radio,"",R.drawable.wifi_tethering_fill0)
|
||||
}
|
||||
if(VERSION.SDK_INT>=33){
|
||||
list += Restriction(UserManager.DISALLOW_ADD_WIFI_CONFIG,R.string.add_wifi_conf,"",R.drawable.wifi_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_CHANGE_WIFI_STATE,R.string.change_wifi_state,"",R.drawable.wifi_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_WIFI_DIRECT,R.string.wifi_direct,"",R.drawable.wifi_tethering_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_WIFI_TETHERING,R.string.wifi_tethering,"",R.drawable.wifi_tethering_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI,R.string.share_admin_wifi,"",R.drawable.share_fill0)
|
||||
}
|
||||
if(VERSION.SDK_INT>=23){ list += Restriction(UserManager.DISALLOW_NETWORK_RESET,R.string.network_reset,"",R.drawable.reset_wrench_fill0) }
|
||||
list += Restriction(UserManager.DISALLOW_CONFIG_TETHERING,R.string.config_tethering,"",R.drawable.wifi_tethering_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_CONFIG_VPN,R.string.config_vpn,"",R.drawable.vpn_key_fill0)
|
||||
if(VERSION.SDK_INT>=29){list += Restriction(UserManager.DISALLOW_CONFIG_PRIVATE_DNS,R.string.config_private_dns,"",R.drawable.dns_fill0)}
|
||||
if(VERSION.SDK_INT>=28){list += Restriction(UserManager.DISALLOW_AIRPLANE_MODE,R.string.airplane_mode,"",R.drawable.airplanemode_active_fill0)}
|
||||
list += Restriction(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,R.string.config_cell_broadcasts,"",R.drawable.cell_tower_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_SMS,R.string.sms,"",R.drawable.sms_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_OUTGOING_CALLS,R.string.outgoing_calls,"",R.drawable.phone_forwarded_fill0)
|
||||
return list
|
||||
}
|
||||
fun connectivity(myContext:Context):List<Restriction>{
|
||||
val list:MutableList<Restriction> = mutableListOf()
|
||||
if(VERSION.SDK_INT>=26){
|
||||
list += Restriction(UserManager.DISALLOW_BLUETOOTH,R.string.bluetooth,"",R.drawable.bluetooth_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_BLUETOOTH_SHARING,R.string.bt_share,"",R.drawable.bluetooth_searching_fill0)
|
||||
}
|
||||
list += Restriction(UserManager.DISALLOW_SHARE_LOCATION,R.string.share_location,"",R.drawable.location_on_fill0)
|
||||
if(VERSION.SDK_INT>=28){list += Restriction(UserManager.DISALLOW_CONFIG_LOCATION,R.string.config_location,"",R.drawable.location_on_fill0)}
|
||||
if(VERSION.SDK_INT>=22){list += Restriction(UserManager.DISALLOW_OUTGOING_BEAM,R.string.outgoing_beam,"",R.drawable.nfc_fill0)}
|
||||
list += Restriction(UserManager.DISALLOW_USB_FILE_TRANSFER,R.string.usb_file_transfer,"",R.drawable.usb_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,R.string.mount_physical_media, "", R.drawable.sd_card_fill0)
|
||||
if(VERSION.SDK_INT>=28){list += Restriction(UserManager.DISALLOW_PRINTING,R.string.printing,"",R.drawable.print_fill0)}
|
||||
return list
|
||||
}
|
||||
fun application(myContext: Context):List<Restriction>{
|
||||
val list:MutableList<Restriction> = mutableListOf()
|
||||
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)}
|
||||
list += Restriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,R.string.inst_unknown_src,"",R.drawable.android_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_UNINSTALL_APPS,R.string.uninstall_app,"",R.drawable.delete_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_APPS_CONTROL,R.string.apps_ctrl, myContext.getString(R.string.apps_control_desc),R.drawable.apps_fill0)
|
||||
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 list:MutableList<Restriction> = mutableListOf()
|
||||
if(VERSION.SDK_INT>=28){
|
||||
list += Restriction(UserManager.DISALLOW_CONFIG_BRIGHTNESS,R.string.config_brightness,"",R.drawable.brightness_5_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT,R.string.config_scr_timeout,"",R.drawable.screen_lock_portrait_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_AMBIENT_DISPLAY,R.string.ambient_display,"",R.drawable.brightness_5_fill0)
|
||||
}
|
||||
list += Restriction(UserManager.DISALLOW_ADJUST_VOLUME,R.string.adjust_volume,"",R.drawable.volume_up_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_UNMUTE_MICROPHONE,R.string.unmute_microphone,"",R.drawable.mic_fill0)
|
||||
if(VERSION.SDK_INT>=31){
|
||||
list += Restriction(UserManager.DISALLOW_CAMERA_TOGGLE,R.string.camera_toggle,"",R.drawable.cameraswitch_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_MICROPHONE_TOGGLE,R.string.microphone_toggle,"",R.drawable.mic_fill0)
|
||||
}
|
||||
return list
|
||||
}
|
||||
fun user():List<Restriction>{
|
||||
val list:MutableList<Restriction> = mutableListOf()
|
||||
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)
|
||||
if(VERSION.SDK_INT>=28){list += Restriction(UserManager.DISALLOW_USER_SWITCH,R.string.switch_user,"",R.drawable.account_circle_fill0)}
|
||||
if(VERSION.SDK_INT>=24){list += Restriction(UserManager.DISALLOW_SET_USER_ICON,R.string.set_user_icon,"",R.drawable.account_circle_fill0)}
|
||||
list += Restriction(UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,R.string.cross_profile_copy, "",R.drawable.content_paste_fill0)
|
||||
if(VERSION.SDK_INT>=28){
|
||||
list += Restriction(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,R.string.share_into_managed_profile,"",R.drawable.share_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_UNIFIED_PASSWORD,R.string.unified_pwd,"",R.drawable.work_fill0)
|
||||
}
|
||||
return list
|
||||
}
|
||||
fun other(myContext: Context):List<Restriction>{
|
||||
val list:MutableList<Restriction> = mutableListOf()
|
||||
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)
|
||||
if(VERSION.SDK_INT>=29){
|
||||
list += Restriction(UserManager.DISALLOW_CONTENT_CAPTURE,R.string.content_capture,"",R.drawable.screenshot_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_CONTENT_SUGGESTIONS,R.string.content_suggestions,"",R.drawable.sms_fill0)
|
||||
}
|
||||
list += Restriction(UserManager.DISALLOW_CREATE_WINDOWS,R.string.create_windows, myContext.getString(R.string.create_windows_desc),R.drawable.web_asset)
|
||||
if(VERSION.SDK_INT>=24){list += Restriction(UserManager.DISALLOW_SET_WALLPAPER,R.string.set_wallpaper,"",R.drawable.wallpaper_fill0)}
|
||||
if(VERSION.SDK_INT>=34){ list += Restriction(UserManager.DISALLOW_GRANT_ADMIN,R.string.grant_admin,"",R.drawable.security_fill0) }
|
||||
if(VERSION.SDK_INT>=23){ list += Restriction(UserManager.DISALLOW_FUN,R.string.`fun`, myContext.getString(R.string.fun_desc),R.drawable.stadia_controller_fill0) }
|
||||
list += Restriction(UserManager.DISALLOW_MODIFY_ACCOUNTS,R.string.modify_accounts,"",R.drawable.manage_accounts_fill0)
|
||||
if(VERSION.SDK_INT>=28){
|
||||
list += Restriction(UserManager.DISALLOW_CONFIG_LOCALE,R.string.config_locale,"",R.drawable.language_fill0)
|
||||
list += Restriction(UserManager.DISALLOW_CONFIG_DATE_TIME,R.string.config_date_time,"",R.drawable.schedule_fill0)
|
||||
}
|
||||
if(VERSION.SDK_INT>=28){list += Restriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,R.string.sys_err_dialog,"",R.drawable.warning_fill0)}
|
||||
list += Restriction(UserManager.DISALLOW_FACTORY_RESET,R.string.factory_reset,"",R.drawable.android_fill0)
|
||||
if(VERSION.SDK_INT>=23){ list += Restriction(UserManager.DISALLOW_SAFE_BOOT,R.string.safe_boot,"",R.drawable.security_fill0) }
|
||||
list += Restriction(UserManager.DISALLOW_DEBUGGING_FEATURES,R.string.debug_features,"",R.drawable.adb_fill0)
|
||||
return list
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user