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() private var crossProfilePkg = mutableSetOf() private var keepUninstallPkg = mutableListOf() private var permittedIme = mutableListOf() private var permittedAccessibility = mutableListOf() @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))||(VERSION.SDK_INT>=33&&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()){ 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()){ 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 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()){ 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()){ 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) }