From 7bd9eaaa461d02c2d1de235970d54a6bb9862414 Mon Sep 17 00:00:00 2001
From: BinTianqi <1220958406@qq.com>
Date: Sat, 9 Mar 2024 13:20:40 +0800
Subject: [PATCH] REFACTOR PROJECT
---
app/src/main/AndroidManifest.xml | 4 +-
.../binbin/androidowner/ApplicationManage.kt | 727 ---------------
.../com/binbin/androidowner/DeviceControl.kt | 666 --------------
.../com/binbin/androidowner/MainActivity.kt | 96 +-
.../com/binbin/androidowner/ManagedProfile.kt | 265 ------
.../java/com/binbin/androidowner/Network.kt | 682 --------------
.../java/com/binbin/androidowner/Password.kt | 476 ----------
.../com/binbin/androidowner/Permissions.kt | 460 ----------
.../binbin/androidowner/SystemUpdatePolicy.kt | 145 ---
.../main/java/com/binbin/androidowner/User.kt | 380 --------
.../java/com/binbin/androidowner/Utils.kt | 26 +
.../{Receiver.kt => dpm/AdminReceiver.kt} | 2 +-
.../androidowner/dpm/ApplicationManage.kt | 798 ++++++++++++++++
.../java/com/binbin/androidowner/dpm/DPM.kt | 26 +
.../binbin/androidowner/dpm/ManagedProfile.kt | 282 ++++++
.../com/binbin/androidowner/dpm/Network.kt | 741 +++++++++++++++
.../com/binbin/androidowner/dpm/Password.kt | 484 ++++++++++
.../binbin/androidowner/dpm/Permissions.kt | 457 ++++++++++
.../androidowner/{ => dpm}/ShizukuActivate.kt | 9 +-
.../binbin/androidowner/dpm/SystemManage.kt | 848 ++++++++++++++++++
.../com/binbin/androidowner/dpm/UserManage.kt | 419 +++++++++
.../UserRestriction.kt} | 37 +-
.../com/binbin/androidowner/ui/Components.kt | 67 ++
app/src/main/res/values/strings.xml | 8 +-
24 files changed, 4182 insertions(+), 3923 deletions(-)
delete mode 100644 app/src/main/java/com/binbin/androidowner/ApplicationManage.kt
delete mode 100644 app/src/main/java/com/binbin/androidowner/DeviceControl.kt
delete mode 100644 app/src/main/java/com/binbin/androidowner/ManagedProfile.kt
delete mode 100644 app/src/main/java/com/binbin/androidowner/Network.kt
delete mode 100644 app/src/main/java/com/binbin/androidowner/Password.kt
delete mode 100644 app/src/main/java/com/binbin/androidowner/Permissions.kt
delete mode 100644 app/src/main/java/com/binbin/androidowner/SystemUpdatePolicy.kt
delete mode 100644 app/src/main/java/com/binbin/androidowner/User.kt
create mode 100644 app/src/main/java/com/binbin/androidowner/Utils.kt
rename app/src/main/java/com/binbin/androidowner/{Receiver.kt => dpm/AdminReceiver.kt} (99%)
create mode 100644 app/src/main/java/com/binbin/androidowner/dpm/ApplicationManage.kt
create mode 100644 app/src/main/java/com/binbin/androidowner/dpm/DPM.kt
create mode 100644 app/src/main/java/com/binbin/androidowner/dpm/ManagedProfile.kt
create mode 100644 app/src/main/java/com/binbin/androidowner/dpm/Network.kt
create mode 100644 app/src/main/java/com/binbin/androidowner/dpm/Password.kt
create mode 100644 app/src/main/java/com/binbin/androidowner/dpm/Permissions.kt
rename app/src/main/java/com/binbin/androidowner/{ => dpm}/ShizukuActivate.kt (97%)
create mode 100644 app/src/main/java/com/binbin/androidowner/dpm/SystemManage.kt
create mode 100644 app/src/main/java/com/binbin/androidowner/dpm/UserManage.kt
rename app/src/main/java/com/binbin/androidowner/{UserRestrict.kt => dpm/UserRestriction.kt} (91%)
create mode 100644 app/src/main/java/com/binbin/androidowner/ui/Components.kt
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 712b992..1a68182 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -47,7 +47,7 @@
@@ -65,7 +65,7 @@
diff --git a/app/src/main/java/com/binbin/androidowner/ApplicationManage.kt b/app/src/main/java/com/binbin/androidowner/ApplicationManage.kt
deleted file mode 100644
index 2162102..0000000
--- a/app/src/main/java/com/binbin/androidowner/ApplicationManage.kt
+++ /dev/null
@@ -1,727 +0,0 @@
-package com.binbin.androidowner
-
-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.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.material3.*
-import androidx.compose.material3.MaterialTheme.colorScheme
-import androidx.compose.material3.MaterialTheme.typography
-import androidx.compose.runtime.*
-import androidx.compose.runtime.saveable.rememberSaveable
-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.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import androidx.core.content.ContextCompat.startActivity
-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(){
- val myContext = LocalContext.current
- val focusMgr = LocalFocusManager.current
- val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
- val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
- var pkgName by rememberSaveable{ mutableStateOf("") }
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- val isWear = sharedPref.getBoolean("isWear",false)
- val bodyTextStyle = if(isWear){ typography.bodyMedium }else{ typography.bodyLarge }
- val titleColor = colorScheme.onPrimaryContainer
- Column{
- if(!isWear){
- TextField(
- value = pkgName,
- onValueChange = { pkgName = it },
- label = { Text(stringResource(R.string.package_name)) },
- modifier = Modifier.fillMaxWidth().padding(horizontal = 4.dp),
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
- )
- }
- Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
- if(isWear){
- TextField(
- value = pkgName,
- onValueChange = { pkgName = it },
- label = { Text(stringResource(R.string.package_name)) },
- modifier = Modifier.fillMaxWidth().padding(horizontal = 2.dp,vertical = 2.dp),
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
- )
- }else{Spacer(Modifier.padding(vertical = 2.dp))}
- if(VERSION.SDK_INT>=24&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
- Text(text = stringResource(R.string.scope_is_work_profile), style = bodyTextStyle, textAlign = TextAlign.Center,modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp))
- }
-
- Button(
- onClick = {
- val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
- intent.setData(Uri.parse("package:$pkgName"))
- startActivity(myContext,intent,null)
- },
- modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)
- ){
- Text(stringResource(R.string.app_info))
- }
-
- if(VERSION.SDK_INT>=24&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
- AppManageItem(
- R.string.suspend,R.string.place_holder,
- {try{ myDpm.isPackageSuspended(myComponent,pkgName) }
- catch(e:NameNotFoundException){ false }
- catch(e:IllegalArgumentException){ false }}
- ) { b -> myDpm.setPackagesSuspended(myComponent, arrayOf(pkgName), b) }
- }
- if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
- AppManageItem(R.string.hide,R.string.isapphidden_desc, {myDpm.isApplicationHidden(myComponent,pkgName)}) {b-> myDpm.setApplicationHidden(myComponent, pkgName, b)}
- }
- if(VERSION.SDK_INT>=24&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
- AppManageItem(
- R.string.always_on_vpn,R.string.place_holder,{pkgName == myDpm.getAlwaysOnVpnPackage(myComponent)}) {b->
- try {
- myDpm.setAlwaysOnVpnPackage(myComponent, pkgName, b)
- } catch(e: java.lang.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)){
- Column(modifier = sections()){
- var state by remember{mutableStateOf(myDpm.isUninstallBlocked(myComponent,pkgName))}
- Text(text = stringResource(R.string.block_uninstall), style = typography.titleLarge, color = titleColor)
- Text(stringResource(R.string.current_state, stringResource(if(state){R.string.enabled}else{R.string.disabled})))
- Text(text = stringResource(R.string.sometimes_get_wrong_block_uninstall_state), style = bodyTextStyle)
- 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))
- }
- }
- }
- }
-
- if(VERSION.SDK_INT>=30&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
- Column(modifier = sections()){
- var pkgList = myDpm.getUserControlDisabledPackages(myComponent)
- var listText by remember{mutableStateOf("")}
- val refresh = {
- pkgList = myDpm.getUserControlDisabledPackages(myComponent)
- listText = ""
- var count = pkgList.size
- for(pkg in pkgList){ count-=1; listText+=pkg; if(count>0){listText+="\n"} }
- }
- var inited by remember{mutableStateOf(false)}
- if(!inited){refresh();inited=true}
- Text(text = stringResource(R.string.ucd), style = typography.titleLarge, color = titleColor)
- Text(text = stringResource(R.string.ucd_desc), style = bodyTextStyle)
- 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}, style = bodyTextStyle, color = titleColor)
- }
- 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))
- }
- }
- }
-
- if(VERSION.SDK_INT>=23&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
- 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)
- )
- Column(modifier = sections()){
- var inputPermission by remember{mutableStateOf("android.permission.")}
- var currentState by remember{mutableStateOf(grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)])}
- Text(text = stringResource(R.string.permission_manage), style = typography.titleLarge, color = titleColor)
- OutlinedTextField(
- value = inputPermission,
- label = { Text(stringResource(R.string.permission))},
- onValueChange = {inputPermission = it},
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
- modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.dp)
- )
- Text(stringResource(R.string.current_state, currentState?:stringResource(R.string.unknown)), style = bodyTextStyle)
- 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))
- }
- }
- }
-
- if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
- Column(modifier = sections()){
- Text(text = stringResource(R.string.cross_profile_package), style = typography.titleLarge, color = titleColor)
- var list by remember{mutableStateOf("")}
- val refresh = {
- crossProfilePkg = myDpm.getCrossProfilePackages(myComponent)
- list = ""
- var count = crossProfilePkg.size
- for(each in crossProfilePkg){ count-=1; list+=each; if(count>0){list+="\n"} }
- }
- var inited by remember{mutableStateOf(false)}
- if(!inited){refresh();inited=true}
- SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
- Text(text = if(list==""){stringResource(R.string.none)}else{list}, style = bodyTextStyle, color = titleColor)
- }
- 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))
- }
- }
- }
- }
-
- if(isProfileOwner(myDpm)){
- Column(modifier = sections()){
- var pkgList: MutableList
- var list by remember{mutableStateOf("")}
- val refresh = {
- pkgList = myDpm.getCrossProfileWidgetProviders(myComponent)
- list = ""
- var count = pkgList.size
- for(each in pkgList){ count-=1; list+=each; if(count>0){list+="\n"}}
- }
- var inited by remember{mutableStateOf(false)}
- if(!inited){refresh();inited=true}
- Text(text = stringResource(R.string.cross_profile_widget), style = typography.titleLarge, color = titleColor)
- Text(text = stringResource(R.string.cross_profile_widget_desc), style = bodyTextStyle)
- SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
- Text(text = if(list==""){stringResource(R.string.none)}else{list}, style = bodyTextStyle, color = titleColor)
- }
- 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))
- }
- }
- }
- }
-
- if(VERSION.SDK_INT>=34&&isDeviceOwner(myDpm)){
- 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()
- }
- val refreshText = {
- credentialListText = ""
- var count = credentialList.size
- for(item in credentialList){ count-=1; credentialListText+=item; if(count>0){credentialListText+="\n"} }
- }
- var inited by remember{mutableStateOf(false)}
- if(!inited){refreshPolicy(); refreshText(); inited = true}
- Column(modifier = sections()){
- Text(text = stringResource(R.string.credential_manage_policy), style = typography.titleLarge, color = titleColor)
- 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})
- AnimatedVisibility(policyType!=-1) {
- Column {
- Text("应用列表")
- SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
- Text(text = if(credentialListText!=""){ credentialListText }else{ stringResource(R.string.none) }, style = bodyTextStyle, color = titleColor)
- }
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
- Button(
- onClick = {
- if(pkgName!=""){credentialList.add(pkgName)}
- refreshText()
- },
- modifier = Modifier.fillMaxWidth(0.49F)
- ) {
- Text(stringResource(R.string.add))
- }
- Button(
- onClick = {
- if(pkgName!=""){credentialList.remove(pkgName)}
- refreshText()
- },
- 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()
- refreshText()
- }
- },
- modifier = Modifier.fillMaxWidth()
- ) {
- Text(stringResource(R.string.apply))
- }
- }
- }
-
- if(isProfileOwner(myDpm)||isDeviceOwner(myDpm)){
- Column(modifier = sections()) {
- Text(text = stringResource(R.string.permitted_accessibility_app), style = typography.titleLarge, color = titleColor)
- var listText by remember{ mutableStateOf("") }
- val refreshList = {
- listText = ""
- var count = permittedAccessibility.size
- for(eachAccessibility in permittedAccessibility){ count-=1; listText+=eachAccessibility; if(count>0){listText+="\n"} }
- }
- var inited by remember{mutableStateOf(false)}
- if(!inited){
- val getList = myDpm.getPermittedAccessibilityServices(myComponent)
- if(getList!=null){ permittedAccessibility = getList }
- refreshList(); inited=true
- }
- SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
- Text(text = if(listText==""){stringResource(R.string.none)}else{listText}, style = bodyTextStyle, color = titleColor)
- }
- Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
- Button(
- onClick = { permittedAccessibility.add(pkgName); refreshList()},
- modifier = Modifier.fillMaxWidth(0.49F)
- ) {
- Text(stringResource(R.string.add))
- }
- Button(
- onClick = { permittedAccessibility.remove(pkgName); refreshList() },
- 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 }
- refreshList()
- },
- modifier = Modifier.fillMaxWidth()
- ) {
- Text(text = stringResource(R.string.apply))
- }
- }
- }
-
- if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
- Column(modifier = sections()) {
- Text(text = stringResource(R.string.permitted_ime), style = typography.titleLarge, color = titleColor)
- var imeListText by remember{ mutableStateOf("") }
- val refreshList = {
- imeListText = ""
- for(eachIme in permittedIme){ imeListText += "$eachIme \n" }
- }
- var inited by remember{mutableStateOf(false)}
- if(!inited){
- val getList = myDpm.getPermittedInputMethods(myComponent)
- if(getList!=null){ permittedIme = getList }
- refreshList();inited=true
- }
- SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
- Text(text = if(imeListText==""){stringResource(R.string.none)}else{imeListText}, style = bodyTextStyle, color = titleColor)
- }
- Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
- Button(
- onClick = { permittedIme.add(pkgName); refreshList() },
- modifier = Modifier.fillMaxWidth(0.49F)
- ) {
- Text(stringResource(R.string.add))
- }
- Button(
- onClick = { permittedIme.remove(pkgName); refreshList()},
- modifier = Modifier.fillMaxWidth(0.96F)
- ) {
- Text(stringResource(R.string.remove))
- }
- }
- Button(
- onClick = {
- Toast.makeText(myContext, if(myDpm.setPermittedInputMethods(myComponent, permittedIme)){"成功"}else{"失败"}, Toast.LENGTH_SHORT).show()
- val getList = myDpm.getPermittedInputMethods(myComponent)
- if(getList!=null){ permittedIme = getList }
- refreshList()
- },
- modifier = Modifier.fillMaxWidth()
- ) {
- Text(stringResource(R.string.apply))
- }
- }
- }
-
- if(VERSION.SDK_INT>=28&&isDeviceOwner(myDpm)){
- Column(modifier = sections()){
- Text(text = stringResource(R.string.keep_uninstalled_pkgs), style = typography.titleLarge, color = titleColor)
- var listText by remember{mutableStateOf("")}
- val refresh = {
- listText = ""
- var count = keepUninstallPkg.size
- for(each in keepUninstallPkg){ count-=1; listText+=each; if(count>0){listText+="\n"} }
- }
- var inited by remember{mutableStateOf(false)}
- if(!inited){
- val getList = myDpm.getKeepUninstalledPackages(myComponent)
- if(getList!=null){ keepUninstallPkg = getList }
- refresh(); inited=true
- }
- SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
- Text(text = if(listText==""){stringResource(R.string.none)}else{listText}, style = bodyTextStyle, color = titleColor)
- }
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
- Button(
- onClick = {
- keepUninstallPkg.add(pkgName)
- refresh()
- },
- modifier = Modifier.fillMaxWidth(0.49F)
- ){
- Text(stringResource(R.string.add))
- }
- Button(
- onClick = {
- keepUninstallPkg.remove(pkgName)
- refresh()
- },
- 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 }
- Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
- },
- modifier = Modifier.fillMaxWidth()
- ){
- Text(stringResource(R.string.apply))
- }
- }
- }
-
- if(VERSION.SDK_INT>=28){
- 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))
- }
- }
-
- if(VERSION.SDK_INT>=34){
- 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))
- }
- }
-
- Column(modifier = sections()){
- Text(text = stringResource(R.string.uninstall_app), style = typography.titleLarge, color = titleColor)
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
- 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(0.49F)
- ) {
- 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(0.96F)
- ) {
- Text(stringResource(R.string.request_uninstall))
- }
- }
- }
-
- Column(modifier = sections()){
- Text(text = stringResource(R.string.install_app), style = typography.titleLarge, color = titleColor)
- 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){apkSelected{selected = apkUri!=null}}
- AnimatedVisibility(selected) {
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
- Button(
- onClick = { uriToStream(myContext, apkUri){stream -> installPackage(myContext,stream)} },
- modifier = Modifier.fillMaxWidth(0.49F)
- ) {
- 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(0.96F)
- ) {
- Text(stringResource(R.string.request_install))
- }
- }
- }
- }
-
- Spacer(Modifier.padding(30.dp))
- }
- }
-}
-
-@Composable
-private fun AppManageItem(
- itemName:Int,
- itemDesc:Int,
- getMethod:()->Boolean,
- setMethod:(b:Boolean)->Unit
-){
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- Row(
- modifier = sections(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- var enabled by remember{mutableStateOf(getMethod())}
- enabled = getMethod()
- Column(modifier = if(sharedPref.getBoolean("isWear",false)){Modifier.fillMaxWidth(0.65F)}else{Modifier}){
- Text(text = stringResource(itemName), style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
- if(itemDesc!=R.string.place_holder){ Text(stringResource(itemDesc)) }
- }
- Switch(
- checked = enabled,
- onCheckedChange = { setMethod(!enabled); enabled=getMethod() }
- )
- }
-}
-
-@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)
-}
-
-private suspend fun apkSelected(operation:()->Unit){
- while(true){
- delay(500)
- operation()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/binbin/androidowner/DeviceControl.kt b/app/src/main/java/com/binbin/androidowner/DeviceControl.kt
deleted file mode 100644
index ba60421..0000000
--- a/app/src/main/java/com/binbin/androidowner/DeviceControl.kt
+++ /dev/null
@@ -1,666 +0,0 @@
-package com.binbin.androidowner
-
-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.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.foundation.focusable
-import androidx.compose.foundation.isSystemInDarkTheme
-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.Alignment
-import androidx.compose.ui.Modifier
-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.font.FontWeight
-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 kotlinx.coroutines.delay
-
-@Composable
-fun SystemManage(){
- val myContext = LocalContext.current
- val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
- val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- val isWear = sharedPref.getBoolean("isWear",false)
- val titleColor = colorScheme.onPrimaryContainer
- val userManager = myContext.getSystemService(Context.USER_SERVICE) as UserManager
- val bodyTextStyle = if(isWear){typography.bodyMedium}else{typography.bodyLarge}
- val focusMgr = LocalFocusManager.current
- Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
- if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
- DeviceCtrlItem(R.string.disable_cam,R.string.place_holder, R.drawable.photo_camera_fill0,
- {myDpm.getCameraDisabled(null)},{b -> myDpm.setCameraDisabled(myComponent,b)}
- )
- }
- if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
- DeviceCtrlItem(R.string.disable_scrcap,R.string.aosp_scrrec_also_work,R.drawable.screenshot_fill0,
- {myDpm.getScreenCaptureDisabled(null)},{b -> myDpm.setScreenCaptureDisabled(myComponent,b) }
- )
- }
- if(VERSION.SDK_INT>=34&&(isDeviceOwner(myDpm)|| (isProfileOwner(myDpm)&&myDpm.isAffiliatedUser))){
- DeviceCtrlItem(R.string.disable_status_bar,R.string.place_holder,R.drawable.notifications_fill0,
- {myDpm.isStatusBarDisabled},{b -> myDpm.setStatusBarDisabled(myComponent,b) }
- )
- }
- if(isDeviceOwner(myDpm)||(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile)){
- if(VERSION.SDK_INT>=30){
- DeviceCtrlItem(R.string.auto_time,R.string.place_holder,R.drawable.schedule_fill0,
- {myDpm.getAutoTimeEnabled(myComponent)},{b -> myDpm.setAutoTimeEnabled(myComponent,b) }
- )
- DeviceCtrlItem(R.string.auto_timezone,R.string.place_holder,R.drawable.globe_fill0,
- {myDpm.getAutoTimeZoneEnabled(myComponent)},{b -> myDpm.setAutoTimeZoneEnabled(myComponent,b) }
- )
- }else{
- DeviceCtrlItem(R.string.auto_time,R.string.place_holder,R.drawable.schedule_fill0,{myDpm.autoTimeRequired},{b -> myDpm.setAutoTimeRequired(myComponent,b)})
- }
- }
- if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)){
- DeviceCtrlItem(R.string.master_mute,R.string.place_holder,R.drawable.volume_up_fill0,
- {myDpm.isMasterVolumeMuted(myComponent)},{b -> myDpm.setMasterVolumeMuted(myComponent,b) }
- )
- }
- if(VERSION.SDK_INT>=26&&(isDeviceOwner(myDpm)|| isProfileOwner(myDpm))){
- DeviceCtrlItem(R.string.backup_service,R.string.place_holder,R.drawable.backup_fill0,
- {myDpm.isBackupServiceEnabled(myComponent)},{b -> myDpm.setBackupServiceEnabled(myComponent,b) }
- )
- }
- if(VERSION.SDK_INT>=23&&(isDeviceOwner(myDpm)|| isProfileOwner(myDpm))){
- DeviceCtrlItem(R.string.disable_bt_contact_share,R.string.place_holder,R.drawable.account_circle_fill0,
- {myDpm.getBluetoothContactSharingDisabled(myComponent)},{b -> myDpm.setBluetoothContactSharingDisabled(myComponent,b)}
- )
- }
- if(VERSION.SDK_INT>=30&&isDeviceOwner(myDpm)){
- DeviceCtrlItem(R.string.common_criteria_mode,R.string.common_criteria_mode_desc,R.drawable.security_fill0,
- {myDpm.isCommonCriteriaModeEnabled(myComponent)},{b -> myDpm.setCommonCriteriaModeEnabled(myComponent,b)}
- )
- }
- if(VERSION.SDK_INT>=31&&(isDeviceOwner(myDpm)||(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
- if(myDpm.canUsbDataSignalingBeDisabled()){
- DeviceCtrlItem(R.string.usb_signal,R.string.place_holder,R.drawable.usb_fill0,
- {myDpm.isUsbDataSignalingEnabled},{b -> myDpm.isUsbDataSignalingEnabled = b }
- )
- }else{
- Text(text = stringResource(R.string.turn_off_usb_not_support),modifier = Modifier.fillMaxWidth(), style = bodyTextStyle, textAlign = TextAlign.Center)
- }
- }
- Column(modifier = sections()) {
- Text(text = stringResource(R.string.keyguard), style = typography.titleLarge,color = colorScheme.onPrimaryContainer)
- if(VERSION.SDK_INT>=23){
- Text(text = stringResource(R.string.require_no_password_to_disable),style=bodyTextStyle)
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
- 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(0.49F)
- ) {
- 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(0.96F)
- ) {
- Text(stringResource(R.string.enable))
- }
- }
- }
- 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} }) }
- }
-
- if(VERSION.SDK_INT>=24){
- Column(modifier = sections()){
- 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))
- }
- Button(
- onClick = {myDpm.reboot(myComponent)},
- enabled = isDeviceOwner(myDpm),
- modifier = Modifier.fillMaxWidth()
- ) {
- Text(stringResource(R.string.reboot))
- }
- }
- }
-
- if(VERSION.SDK_INT>=28){
- Column(modifier = sections()){
- Text(text = stringResource(R.string.edit_time), style = typography.titleLarge, color = titleColor)
- var inputTime by remember{mutableStateOf("")}
- Text(text = stringResource(R.string.from_epoch_to_target_time), style = bodyTextStyle)
- 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().padding(vertical = 2.dp)
- )
- if(isWear){
- Button(
- onClick = {inputTime = System.currentTimeMillis().toString()},
- modifier = Modifier.fillMaxWidth()
- ) {
- Text(stringResource(R.string.get_current_time))
- }
- }
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
- Button(
- onClick = {myDpm.setTime(myComponent,inputTime.toLong())},
- modifier = Modifier.fillMaxWidth(if(isWear){1F}else{0.35F}),
- enabled = inputTime!=""&&(isDeviceOwner(myDpm)||(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))
- ) {
- Text("应用")
- }
- if(!isWear){
- Button(
- onClick = {inputTime = System.currentTimeMillis().toString()},
- modifier = Modifier.fillMaxWidth(0.98F)
- ) {
- Text(stringResource(R.string.get_current_time))
- }
- }
- }
- }
- }
-
- if(VERSION.SDK_INT>=23&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
- Column(modifier = sections()){
- var selectedPolicy by remember{mutableIntStateOf(myDpm.getPermissionPolicy(myComponent))}
- Text(text = stringResource(R.string.permission_policy), style = typography.titleLarge, color = titleColor)
- 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})
- 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))
- }
- }
- }
-
- if(VERSION.SDK_INT>=34&&isDeviceOwner(myDpm)){
- Column(modifier = sections()){
- Text(text = stringResource(R.string.mte_policy), style = typography.titleLarge, color = titleColor)
- Text(stringResource(R.string.mte_policy_desc))
- 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))
- }
- }
- }
-
- if(VERSION.SDK_INT>=31&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
- Column(modifier = sections()){
- var appPolicy by remember{mutableIntStateOf(myDpm.nearbyAppStreamingPolicy)}
- Text(text = stringResource(R.string.nearby_app_streaming), style = typography.titleLarge, color = titleColor)
- 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})
- Button(
- onClick = {
- myDpm.nearbyAppStreamingPolicy = appPolicy
- Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
- },
- modifier = Modifier.fillMaxWidth()
- ) {
- Text("应用")
- }
- Spacer(Modifier.padding(vertical = 3.dp))
- var notificationPolicy by remember{mutableIntStateOf(myDpm.nearbyNotificationStreamingPolicy)}
- Text(text = stringResource(R.string.nearby_notifi_streaming), style = typography.titleLarge, color = titleColor)
- 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})
- 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))
- }
- }
- }
-
- if(VERSION.SDK_INT>=28&&isDeviceOwner(myDpm)){
- Column(modifier = sections()){
- 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
- }
- }
- Text(text = stringResource(R.string.lock_task_feature), style = typography.titleLarge, color = titleColor)
- 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 = 4.dp))
- val whitelist = myDpm.getLockTaskPackages(myComponent).toMutableList()
- var listText by remember{mutableStateOf("")}
- var inputPkg by remember{mutableStateOf("")}
- val refreshWhitelist = {
- inputPkg=""
- listText=""
- var currentItem = whitelist.size
- for(each in whitelist){
- currentItem-=1
- listText += each
- if(currentItem>0){listText += "\n"}
- }
- }
- refreshWhitelist()
- Text(text = stringResource(R.string.whitelist_app), style = typography.titleLarge, color = titleColor)
- if(listText!=""){
- SelectionContainer {
- Text(text = listText, style = bodyTextStyle)
- }
- }else{
- Text(text = stringResource(R.string.none), style = bodyTextStyle)
- }
- 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))
- }
- }
- }
- }
-
- if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
- var exist by remember{mutableStateOf(false)}
- var isEmpty by remember{mutableStateOf(false)}
- val refresh = {
- isEmpty = caCert.isEmpty()
- exist = if(!isEmpty){ myDpm.hasCaCertInstalled(myComponent, caCert) }else{ false }
- }
- LaunchedEffect(exist){ isCaCertSelected(600){refresh()} }
- Column(modifier = sections()){
- Text(text = stringResource(R.string.ca_cert), style = typography.titleLarge, color = titleColor)
- if(isEmpty){ Text(text = stringResource(R.string.please_select_ca_cert)) }else{ Text(text = stringResource(R.string.cacert_installed, exist)) }
- 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))
- }
- }
- }
-
- if(VERSION.SDK_INT>=26&&(isDeviceOwner(myDpm)||(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
- Column(modifier = sections()){
- Text(text = stringResource(R.string.retrieve_security_logs), style = typography.titleLarge, color = titleColor)
- Text(text = stringResource(R.string.developing), style = bodyTextStyle)
- Row(modifier=Modifier.fillMaxWidth().padding(horizontal=8.dp),horizontalArrangement=Arrangement.SpaceBetween,verticalAlignment=Alignment.CenterVertically){
- var checked by remember{mutableStateOf(myDpm.isSecurityLoggingEnabled(myComponent))}
- Text(text = stringResource(R.string.enabled), style = typography.titleLarge)
- Switch(
- checked = checked,
- onCheckedChange = {myDpm.setSecurityLoggingEnabled(myComponent,!checked);checked=myDpm.isSecurityLoggingEnabled(myComponent)}
- )
- }
- 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))
- }
- }
- }
-
- if(isDeviceOwner(myDpm)){
- SysUpdatePolicy()
- }
-
- Column(modifier = sections(if(isSystemInDarkTheme()){ colorScheme.errorContainer }else{ colorScheme.errorContainer.copy(alpha = 0.6F) })) {
- 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("")}
- Text(text = stringResource(R.string.wipe_data),style = typography.titleLarge,modifier = Modifier.padding(6.dp),color = colorScheme.onErrorContainer)
- CheckBoxItem(stringResource(R.string.wipe_external_storage),{externalStorage},{externalStorage=!externalStorage;confirmed=false}, colorScheme.onErrorContainer)
- if(VERSION.SDK_INT>=22&&isDeviceOwner(myDpm)){
- CheckBoxItem(stringResource(R.string.wipe_reset_protection_data),{protectionData},{protectionData=!protectionData;confirmed=false}, colorScheme.onErrorContainer)
- }
- if(VERSION.SDK_INT>=28){ CheckBoxItem(stringResource(R.string.wipe_euicc),{euicc},{euicc=!euicc;confirmed=false}, colorScheme.onErrorContainer) }
- if(VERSION.SDK_INT>=29){ CheckBoxItem(stringResource(R.string.wipe_silently),{silent},{silent=!silent;confirmed=false}, colorScheme.onErrorContainer) }
- 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)
- )
- }
- 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 }))
- }
- Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {
- 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(if(VERSION.SDK_INT >= 34&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){0.49F}else{1F})
- ) {
- 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(0.96F)
- ) {
- Text("WipeDevice")
- }
- }
- }
- if(VERSION.SDK_INT>=24&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
- Text(text = stringResource(R.string.will_delete_work_profile), style = bodyTextStyle)
- }
- if(VERSION.SDK_INT>=34&&Binder.getCallingUid()/100000==0){
- Text(text = stringResource(R.string.api34_or_above_wipedata_cannot_in_system_user), style = bodyTextStyle)
- }
- }
- Spacer(Modifier.padding(vertical = 30.dp))
- }
-}
-
-@Composable
-fun DeviceCtrlItem(
- itemName:Int,
- itemDesc:Int,
- leadIcon:Int,
- getMethod:()->Boolean,
- setMethod:(b:Boolean)->Unit
-){
- var isEnabled by remember{ mutableStateOf(false) }
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- val isWear = sharedPref.getBoolean("isWear",false)
- Row(
- modifier = sections(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Row(
- verticalAlignment = Alignment.CenterVertically,
- modifier = if(isWear){Modifier.fillMaxWidth(0.65F)}else{Modifier.fillMaxWidth(0.8F)}
- ){
- if(!isWear){
- Icon(
- painter = painterResource(leadIcon),
- contentDescription = null,
- tint = colorScheme.onPrimaryContainer,
- modifier = Modifier.padding(start = 5.dp, end = 9.dp)
- )}
- Column {
- Text(
- text = stringResource(itemName),
- style = if(!isWear){typography.titleLarge}else{typography.titleMedium},
- color = colorScheme.onPrimaryContainer,
- fontWeight = if(isWear){ FontWeight.SemiBold }else{ FontWeight.Medium }
- )
- if(itemDesc!=R.string.place_holder){ Text(stringResource(itemDesc)) }
- }
- }
- isEnabled = getMethod()
- Switch(
- checked = isEnabled,
- onCheckedChange = {
- setMethod(!isEnabled)
- isEnabled=getMethod()
- },
- modifier = Modifier.padding(end = 5.dp)
- )
- }
-}
-
-private suspend fun isCaCertSelected(delay:Long,operation:()->Unit){
- while(true){
- delay(delay)
- operation()
- }
-}
diff --git a/app/src/main/java/com/binbin/androidowner/MainActivity.kt b/app/src/main/java/com/binbin/androidowner/MainActivity.kt
index babfa9f..0bc52ce 100644
--- a/app/src/main/java/com/binbin/androidowner/MainActivity.kt
+++ b/app/src/main/java/com/binbin/androidowner/MainActivity.kt
@@ -5,15 +5,12 @@ import android.app.Activity
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
-import android.content.Intent
-import android.net.Uri
import android.os.Build.VERSION
import android.os.Bundle
import android.os.UserManager
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
-import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.*
@@ -40,27 +37,14 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
+import com.binbin.androidowner.dpm.*
import com.binbin.androidowner.ui.theme.AndroidOwnerTheme
import com.binbin.androidowner.ui.theme.Animations
-import java.io.FileNotFoundException
-import java.io.IOException
-import java.io.InputStream
-lateinit var getCaCert: ActivityResultLauncher
-lateinit var createUser:ActivityResultLauncher
-lateinit var createManagedProfile:ActivityResultLauncher
-lateinit var getApk:ActivityResultLauncher
-lateinit var getUserIcon:ActivityResultLauncher
-var userIconUri:Uri? = null
-var apkUri: Uri? = null
-var caCert = byteArrayOf()
-
@ExperimentalMaterial3Api
class MainActivity : ComponentActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- WindowCompat.setDecorFitsSystemWindows(window, false)
- super.onCreate(savedInstanceState)
+ private fun registerActivityResult(){
getUserIcon = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
userIconUri = it.data?.data
if(userIconUri==null){ Toast.makeText(applicationContext, "空URI", Toast.LENGTH_SHORT).show() }
@@ -87,6 +71,12 @@ class MainActivity : ComponentActivity() {
createManagedProfile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if(it.resultCode==Activity.RESULT_CANCELED){Toast.makeText(applicationContext, "用户已取消", Toast.LENGTH_SHORT).show()}
}
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ WindowCompat.setDecorFitsSystemWindows(window, false)
+ super.onCreate(savedInstanceState)
+ registerActivityResult()
setContent {
AndroidOwnerTheme {
MyScaffold()
@@ -160,7 +150,7 @@ fun MyScaffold(){
popExitTransition = Animations(myContext).navHostPopExitTransition
){
composable(route = "HomePage", content = { HomePage(navCtrl)})
- composable(route = "DeviceControl", content = { SystemManage()})
+ composable(route = "SystemManage", content = { SystemManage() })
composable(route = "ManagedProfile", content = {ManagedProfile()})
composable(route = "Permissions", content = { DpmPermissions(navCtrl)})
composable(route = "ApplicationManage", content = { ApplicationManage()})
@@ -254,57 +244,6 @@ fun HomePageItem(name:Int, imgVector:Int, navTo:String, myNav:NavHostController)
}
}
-@Composable
-fun RadioButtonItem(
- text:String,
- selected:()->Boolean,
- operation:()->Unit,
- textColor:Color = colorScheme.onBackground
-){
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- val isWear = sharedPref.getBoolean("isWear",false)
- Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier
- .fillMaxWidth()
- .padding(vertical = if(isWear){3.dp}else{0.dp})
- .clip(RoundedCornerShape(25))
- .clickable(onClick = operation)
- ) {
- RadioButton(selected = selected(), onClick = operation,modifier=if(isWear){Modifier.size(28.dp)}else{Modifier})
- Text(text = text, style = if(!isWear){typography.bodyLarge}else{typography.bodyMedium}, color = textColor,
- modifier = Modifier.padding(bottom = 2.dp))
- }
-}
-@Composable
-fun CheckBoxItem(
- text:String,
- checked:()->Boolean,
- operation:()->Unit,
- textColor:Color = colorScheme.onBackground
-){
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- val isWear = sharedPref.getBoolean("isWear",false)
- Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier
- .fillMaxWidth()
- .padding(vertical = if(isWear){3.dp}else{0.dp})
- .clip(RoundedCornerShape(25))
- .clickable(onClick = operation)
- ) {
- Checkbox(
- checked = checked(),
- onCheckedChange = {operation()},
- modifier=if(isWear){Modifier.size(28.dp)}else{Modifier}
- )
- Text(text = text, style = if(!isWear){typography.bodyLarge}else{typography.bodyMedium}, color = textColor, modifier = Modifier.padding(bottom = 2.dp))
- }
-}
-
-fun isDeviceOwner(dpm:DevicePolicyManager): Boolean {
- return dpm.isDeviceOwnerApp("com.binbin.androidowner")
-}
-
-fun isProfileOwner(dpm:DevicePolicyManager): Boolean {
- return dpm.isProfileOwnerApp("com.binbin.androidowner")
-}
@SuppressLint("ModifierFactoryExtensionFunction", "ComposableModifierFactory")
@Composable
@@ -330,20 +269,3 @@ fun sections(bgColor:Color=colorScheme.primaryContainer,onClick:()->Unit={},clic
}
}
-fun uriToStream(
- context: Context,
- uri: Uri?,
- operation:(stream:InputStream)->Unit
-){
- if(uri!=null){
- apkUri = uri
- try{
- val stream = context.contentResolver.openInputStream(uri)
- if(stream!=null) { operation(stream) }
- else{ Toast.makeText(context, "空的流", Toast.LENGTH_SHORT).show() }
- stream?.close()
- }
- catch(e:FileNotFoundException){ Toast.makeText(context, "文件不存在", Toast.LENGTH_SHORT).show() }
- catch(e:IOException){ Toast.makeText(context, "IO异常", Toast.LENGTH_SHORT).show() }
- }else{ Toast.makeText(context, "空URI", Toast.LENGTH_SHORT).show() }
-}
diff --git a/app/src/main/java/com/binbin/androidowner/ManagedProfile.kt b/app/src/main/java/com/binbin/androidowner/ManagedProfile.kt
deleted file mode 100644
index afb0434..0000000
--- a/app/src/main/java/com/binbin/androidowner/ManagedProfile.kt
+++ /dev/null
@@ -1,265 +0,0 @@
-package com.binbin.androidowner
-
-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.animation.animateContentSize
-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.Button
-import androidx.compose.material3.MaterialTheme.colorScheme
-import androidx.compose.material3.MaterialTheme.typography
-import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.Switch
-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 androidx.navigation.NavHostController
-
-@Composable
-fun ManagedProfile() {
- val myContext = LocalContext.current
- val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
- val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
- val focusMgr = LocalFocusManager.current
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- val isWear = sharedPref.getBoolean("isWear",false)
- val bodyTextStyle = if(isWear){ typography.bodyMedium}else{ typography.bodyLarge}
- val titleColor = colorScheme.onPrimaryContainer
- Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())){
-
- Column(modifier = sections()){
- Text(text = stringResource(R.string.info), style = typography.titleLarge, color = titleColor)
- if(VERSION.SDK_INT>=24){
- if(isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
- Text(text = stringResource(R.string.is_already_work_profile))
- }else{
- Text(text = stringResource(R.string.able_to_create_work_profile, myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)), style = bodyTextStyle)
- if(isDeviceOwner(myDpm)){
- Text(text = stringResource(R.string.device_owner_cannot_create_work_profile), style = bodyTextStyle)
- }
- }
- }
- if(VERSION.SDK_INT>=30){
- Text(text = stringResource(R.string.is_org_owned_profile, myDpm.isOrganizationOwnedDeviceWithManagedProfile), style = bodyTextStyle)
- }
- if(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent))){
- Button(
- onClick = { myContext.startActivity(Intent("com.binbin.androidowner.MAIN_ACTION")) }, modifier = Modifier.fillMaxWidth()
- ){
- Text("跳转至个人应用")
- }
- }else{
- if(!myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)&&!isDeviceOwner(myDpm)){
- Button(
- onClick = { myContext.startActivity(Intent("com.binbin.androidowner.MAIN_ACTION")) }, modifier = Modifier.fillMaxWidth()
- ){
- Text("跳转至工作资料")
- }
- }
- }
- }
-
- if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)&&!myDpm.isOrganizationOwnedDeviceWithManagedProfile){
- var expand by remember{mutableStateOf(false)}
- Column(modifier = sections(colorScheme.tertiaryContainer,{expand=true},!expand).animateContentSize(animationSpec = scrollAnim())){
- if(expand){
- Text(text = stringResource(R.string.org_owned_work_profile), color = colorScheme.onTertiaryContainer, style = typography.titleLarge)
- SelectionContainer {
- Text(text = "使用ADB执行以下命令,或者使用Shizuku")
- Text(
- text = stringResource(R.string.activate_org_profile_command, Binder.getCallingUid()/100000),
- color = colorScheme.onTertiaryContainer, style = bodyTextStyle
- )
- }
- }else{
- Text(text = stringResource(R.string.become_org_profile), color = colorScheme.onTertiaryContainer)
- Text(text = stringResource(R.string.touch_to_view_command), style = bodyTextStyle)
- }
- }
- }
- if(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE))){
- Column(modifier = sections()) {
- Text(text = stringResource(R.string.work_profile), style = typography.titleLarge, color = titleColor)
- var skipEncrypt by remember{mutableStateOf(false)}
- if(VERSION.SDK_INT>=24){CheckBoxItem(stringResource(R.string.skip_encryption),{skipEncrypt},{skipEncrypt=!skipEncrypt})}
- 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,"com.binbin.androidowner")
- }
- 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))
- }
- }
- }
-
- if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile){
- Row(modifier = sections(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically){
- var suspended by remember{mutableStateOf(false)}
- suspended = myDpm.getPersonalAppsSuspendedReasons(myComponent)!=PERSONAL_APPS_NOT_SUSPENDED
- Text(text = stringResource(R.string.suspend_personal_app), style = typography.titleLarge, color = titleColor)
- Switch(
- checked = suspended,
- onCheckedChange ={
- myDpm.setPersonalAppsSuspended(myComponent,!suspended)
- suspended = myDpm.getPersonalAppsSuspendedReasons(myComponent)!=PERSONAL_APPS_NOT_SUSPENDED
- }
- )
- }
- }
-
- if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile){
- Column(modifier = sections()){
- var time by remember{mutableStateOf("")}
- time = myDpm.getManagedProfileMaximumTimeOff(myComponent).toString()
- Text(text = stringResource(R.string.profile_max_time_off), style = typography.titleLarge, color = titleColor)
- Text(text = stringResource(R.string.profile_max_time_out_desc), style = bodyTextStyle)
- 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), style = bodyTextStyle)
- 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))
- }
- }
- }
-
- if(isProfileOwner(myDpm)&&(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isManagedProfile(myComponent)))){
- Column(modifier = sections()){
- var action by remember{mutableStateOf("")}
- Text(text = stringResource(R.string.intent_filter), style = typography.titleLarge, color = titleColor)
- 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().padding(vertical = 2.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))
- }
- Button(
- onClick = {
- myDpm.clearCrossProfileIntentFilters(myComponent)
- myDpm.addCrossProfileIntentFilter(myComponent, IntentFilter("com.binbin.androidowner.MAIN_ACTION"), FLAG_MANAGED_CAN_ACCESS_PARENT)
- myDpm.addCrossProfileIntentFilter(myComponent, IntentFilter("com.binbin.androidowner.MAIN_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.clear_cross_profile_filters))
- }
- }
- }
-
- if(VERSION.SDK_INT>=31&&(isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent))){
- Column(modifier = sections()){
- var orgId by remember{mutableStateOf("")}
- Text(text = stringResource(R.string.org_id), style = typography.titleLarge, color = titleColor)
- 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().padding(vertical = 2.dp)
- )
- AnimatedVisibility(orgId.length !in 6..64) {
- Text(text = stringResource(R.string.length_6_to_64), style = bodyTextStyle)
- }
- 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))
- }
- Text(text = stringResource(R.string.get_specific_id_after_set_org_id), style = bodyTextStyle)
- }
- }
-
- Spacer(Modifier.padding(vertical = 30.dp))
- }
-}
-
-@Composable
-fun ActivateManagedProfile(navCtrl: NavHostController){
- val myContext = LocalContext.current
- val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
- val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
- val sharedPref = myContext.getSharedPreferences("data", Context.MODE_PRIVATE)
- myDpm.addCrossProfileIntentFilter(myComponent, IntentFilter("com.binbin.androidowner.MAIN_ACTION"), FLAG_MANAGED_CAN_ACCESS_PARENT)
- myDpm.addCrossProfileIntentFilter(myComponent, IntentFilter("com.binbin.androidowner.MAIN_ACTION"), FLAG_PARENT_CAN_ACCESS_MANAGED)
- Column(modifier = Modifier.verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally){
- Text(text = stringResource(R.string.activate_managed_profile), style = typography.titleLarge)
- Text(text = stringResource(R.string.activate_managed_profile_desc))
- Button(
- onClick = {
- myDpm.setProfileEnabled(myComponent)
- navCtrl.popBackStack("HomePage",false)
- sharedPref.edit().putBoolean("ManagedProfileActivated",true).apply()
- Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
- },
- modifier = Modifier.fillMaxWidth().padding(8.dp)
- ) {
- Text(stringResource(R.string.activate))
- }
- }
-}
diff --git a/app/src/main/java/com/binbin/androidowner/Network.kt b/app/src/main/java/com/binbin/androidowner/Network.kt
deleted file mode 100644
index e30acea..0000000
--- a/app/src/main/java/com/binbin/androidowner/Network.kt
+++ /dev/null
@@ -1,682 +0,0 @@
-package com.binbin.androidowner
-
-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.content.Context
-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.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.material3.Button
-import androidx.compose.material3.MaterialTheme.colorScheme
-import androidx.compose.material3.MaterialTheme.typography
-import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.Switch
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextField
-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.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import androidx.core.net.toUri
-
-var ssidSet = mutableSetOf()
-@Composable
-fun Network(){
- Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())){
- val myContext = LocalContext.current
- val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
- val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- val isWear = sharedPref.getBoolean("isWear",false)
- val bodyTextStyle = if(isWear){ typography.bodyMedium }else{ typography.bodyLarge }
- val focusMgr = LocalFocusManager.current
- val titleColor = colorScheme.onPrimaryContainer
-
- if(VERSION.SDK_INT>=24){
- val wifimac = try { myDpm.getWifiMacAddress(myComponent).toString() }catch(e:SecurityException){ "没有权限" }
- Text(text = "WiFi MAC: $wifimac",modifier=Modifier.fillMaxWidth(), textAlign = TextAlign.Center,style=bodyTextStyle)
- }
-
- if(VERSION.SDK_INT>=33&&isDeviceOwner(myDpm)){
- DeviceCtrlItem(R.string.preferential_network_service,R.string.developing,R.drawable.globe_fill0,
- {myDpm.isPreferentialNetworkServiceEnabled},{b -> myDpm.isPreferentialNetworkServiceEnabled = b}
- )
- }
- if(VERSION.SDK_INT>=30&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
- DeviceCtrlItem(R.string.wifi_lockdown,R.string.place_holder,R.drawable.wifi_password_fill0,
- {myDpm.hasLockdownAdminConfiguredNetworks(myComponent)},{b -> myDpm.setConfiguredNetworksLockdownState(myComponent,b)}
- )
- }
- if(VERSION.SDK_INT>=33){
- Column(modifier = sections()){
- var selectedWifiSecLevel by remember{mutableIntStateOf(myDpm.minimumRequiredWifiSecurityLevel)}
- Text(text = stringResource(R.string.min_wifi_security_level), style = typography.titleLarge, color = titleColor)
- RadioButtonItem(stringResource(R.string.wifi_security_level_open), {selectedWifiSecLevel==DevicePolicyManager.WIFI_SECURITY_OPEN}, {selectedWifiSecLevel= DevicePolicyManager.WIFI_SECURITY_OPEN})
- RadioButtonItem("WEP, WPA(2)-PSK", {selectedWifiSecLevel==DevicePolicyManager.WIFI_SECURITY_PERSONAL}, {selectedWifiSecLevel= DevicePolicyManager.WIFI_SECURITY_PERSONAL})
- RadioButtonItem("WPA-EAP", {selectedWifiSecLevel==DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_EAP}, {selectedWifiSecLevel= DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_EAP})
- RadioButtonItem("WPA3-192bit", {selectedWifiSecLevel==DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_192}, {selectedWifiSecLevel= DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_192})
- 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))
- }
- }
- }
-
- if(VERSION.SDK_INT>=33&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
- Column(modifier = sections()){
- 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()
- }
- val refreshList = {
- ssidList = ""
- var count = ssidSet.size
- for(ssid in ssidSet){ count-=1; ssidList+=ssid; if(count>0){ssidList+="\n"} }
- }
- var inited by remember{mutableStateOf(false)}
- if(!inited){ refreshPolicy(); refreshList(); inited=true }
- Text(text = stringResource(R.string.wifi_ssid_policy), style = typography.titleLarge, color = titleColor)
- 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!=""){
- Text(stringResource(R.string.ssid_list_is))
- SelectionContainer{
- Text(text = ssidList, style = bodyTextStyle, color = colorScheme.onPrimaryContainer)
- }
- }
- }
- OutlinedTextField(
- value = inputSsid,
- label = { Text("SSID")},
- onValueChange = {inputSsid = it},
- keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
- modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.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()))
- refreshList()
- }
- 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 = ""
- refreshList()
- }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))
- }
- }
- }
- if(VERSION.SDK_INT>=29&&isDeviceOwner(myDpm)){
- Column(modifier = sections()){
- Text(text = stringResource(R.string.private_dns), style = typography.titleLarge, color = titleColor)
- val dnsStatus = mapOf(
- DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN to stringResource(R.string.unknown),
- DevicePolicyManager.PRIVATE_DNS_MODE_OFF to stringResource(R.string.disabled),
- DevicePolicyManager.PRIVATE_DNS_MODE_OPPORTUNISTIC to stringResource(R.string.auto),
- DevicePolicyManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME to stringResource(R.string.dns_provide_hostname)
- )
- val operationResult = mapOf(
- DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR to stringResource(R.string.success),
- DevicePolicyManager.PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING to stringResource(R.string.host_not_serving_dns_tls),
- DevicePolicyManager.PRIVATE_DNS_SET_ERROR_FAILURE_SETTING to stringResource(R.string.fail)
- )
- var status by remember{mutableStateOf(dnsStatus[myDpm.getGlobalPrivateDnsMode(myComponent)])}
- Text(text = stringResource(R.string.current_state, status?:stringResource(R.string.unknown)))
- 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 = 3.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().padding(vertical = 2.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))
- }
- }
- }
-
- if(VERSION.SDK_INT>=26&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)))){
- Column(modifier = sections()){
- Text(text = stringResource(R.string.retrieve_net_logs), style = typography.titleLarge, color = titleColor)
- Text(text = stringResource(R.string.developing), style = bodyTextStyle)
- Row(modifier=Modifier.fillMaxWidth().padding(horizontal=8.dp),horizontalArrangement=Arrangement.SpaceBetween,verticalAlignment=Alignment.CenterVertically){
- var checked by remember{mutableStateOf(myDpm.isNetworkLoggingEnabled(myComponent))}
- Text(text = stringResource(R.string.enabled), style = typography.titleLarge)
- Switch(
- checked = checked,
- onCheckedChange = {myDpm.setNetworkLoggingEnabled(myComponent,!checked);checked = myDpm.isNetworkLoggingEnabled(myComponent)}
- )
- }
- 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))
- }
- }
- }
-
- if(VERSION.SDK_INT>=31&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
- Column(modifier = sections()){
- var keyPair by remember{mutableStateOf("")}
- Text(text = stringResource(R.string.wifi_keypair), style = typography.titleLarge, color = titleColor)
- 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().padding(vertical = 2.dp)
- )
- val isExist = try{myDpm.isKeyPairGrantedToWifiAuth(keyPair)}catch(e:java.lang.IllegalArgumentException){false}
- Text(stringResource(R.string.already_exist)+":$isExist")
- 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))
- }
- }
- }
- }
-
- if(VERSION.SDK_INT>=28&&isDeviceOwner(myDpm)){
- Column(modifier = sections()){
- val setting = myDpm.getOverrideApns(myComponent)
- var inputNum by remember{mutableStateOf("0")}
- var nextStep by remember{mutableStateOf(false)}
- val builder = Builder()
- Text(text = stringResource(R.string.apn_settings), style = typography.titleLarge, color = titleColor)
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically){
- Text(text = stringResource(R.string.enable), style = typography.titleLarge)
- Switch(checked = myDpm.isOverrideApnEnabled(myComponent), onCheckedChange = {myDpm.setOverrideApnsEnabled(myComponent,it)})
- }
- Text(text = stringResource(R.string.total_apn_amount, setting.size), style = bodyTextStyle)
- if(setting.size>0){
- Text(text = stringResource(R.string.select_a_apn_or_create, setting.size), style = bodyTextStyle)
- 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), style = bodyTextStyle)
- }
- 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))
- }
- }
- }
- }
- }
- }
- Text(text = stringResource(id = R.string.developing), style = bodyTextStyle)
- }
- }
- Spacer(Modifier.padding(vertical = 30.dp))
- }
-}
diff --git a/app/src/main/java/com/binbin/androidowner/Password.kt b/app/src/main/java/com/binbin/androidowner/Password.kt
deleted file mode 100644
index 226133d..0000000
--- a/app/src/main/java/com/binbin/androidowner/Password.kt
+++ /dev/null
@@ -1,476 +0,0 @@
-package com.binbin.androidowner
-
-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.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.material.icons.Icons
-import androidx.compose.material.icons.outlined.Check
-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.clip
-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 kotlinx.coroutines.delay
-
-@Composable
-fun Password(){
- val myContext = LocalContext.current
- val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
- val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- var newPwd by remember{ mutableStateOf("") }
- val focusMgr = LocalFocusManager.current
- val isWear = sharedPref.getBoolean("isWear",false)
- val titleColor = colorScheme.onPrimaryContainer
- val bodyTextStyle = if(isWear){typography.bodyMedium}else{typography.bodyLarge}
- val scrollState = rememberScrollState()
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier.fillMaxSize().verticalScroll(scrollState)
- ) {
- 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)) }
- Text(
- text = stringResource(R.string.password_warning),
- color = colorScheme.onErrorContainer,
- modifier = sections(colorScheme.errorContainer),
- style=bodyTextStyle
- )
- if(isWear){
- Text(
- text = stringResource(R.string.password_wearos_warning),
- color = colorScheme.onErrorContainer,
- modifier = sections(colorScheme.errorContainer),
- style = typography.bodyMedium
- )
- }
- if(myDpm.isDeviceOwnerApp("com.binbin.androidowner")){
- Column(modifier = sections()) {
- 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)),style=bodyTextStyle)
- }
- if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)){
- Text(stringResource(R.string.is_password_sufficient, myDpm.isActivePasswordSufficient),style=bodyTextStyle)
- }
- val pwdFailedAttempts = myDpm.currentFailedPasswordAttempts
- Text(text = stringResource(R.string.password_failed_attempts_is, pwdFailedAttempts),style=bodyTextStyle)
- if(VERSION.SDK_INT>=28&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
- val unifiedPwd = myDpm.isUsingUnifiedPassword(myComponent)
- Text(stringResource(R.string.is_using_unified_password, unifiedPwd),style=bodyTextStyle)
- }
- }
- }
- if(VERSION.SDK_INT>=26){
- Column(horizontalAlignment = Alignment.Start, modifier = sections()) {
- Text(text = stringResource(R.string.reset_password_token), style = typography.titleLarge,color = titleColor)
- Row(
- modifier = if(!isWear){Modifier.fillMaxWidth()}else{Modifier.horizontalScroll(rememberScrollState())},
- horizontalArrangement = Arrangement.SpaceBetween
- ){
- 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 = if(isWear){Modifier}else{Modifier.fillMaxWidth(0.32F)},
- enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm)
- ) {
- Text(stringResource(R.string.clear))
- }
- if(isWear){Spacer(Modifier.padding(horizontal = 2.dp))}
- 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 = if(isWear){Modifier}else{Modifier.fillMaxWidth(0.47F)}
- ) {
- Text(stringResource(R.string.set))
- }
- if(isWear){Spacer(Modifier.padding(horizontal = 2.dp))}
- 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 = if(isWear){Modifier}else{Modifier.fillMaxWidth(0.88F)}
- ) {
- Text(stringResource(R.string.activate))
- }
- }
- Text(stringResource(R.string.activate_token_not_required_when_no_password),style=bodyTextStyle)
- }
- }
- Column(
- modifier = sections()
- ) {
- var confirmed by remember{ mutableStateOf(false) }
- Text(text = stringResource(R.string.reset_password),style = typography.titleLarge,color = titleColor)
- 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().padding(vertical = if(isWear){0.dp}else{5.dp}).fillMaxWidth()
- )
- Text(text = stringResource(R.string.reset_pwd_desc), modifier = Modifier.padding(vertical = 3.dp),style=bodyTextStyle)
- var resetPwdFlag by remember{ mutableIntStateOf(0) }
- 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})
- 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}))
- }
- 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))
- }
- }
-
- PasswordItem(R.string.max_pwd_fail,R.string.max_pwd_fail_desc,R.string.max_pwd_fail_textfield, false,
- {myDpm.getMaximumFailedPasswordsForWipe(null).toString()},{ic -> myDpm.setMaximumFailedPasswordsForWipe(myComponent, ic.toInt()) })
- PasswordItem(R.string.pwd_timeout,R.string.pwd_timeout_desc,R.string.pwd_timeout_textfield,true,
- {myDpm.getPasswordExpiration(null).toString()},{ic -> myDpm.setPasswordExpirationTimeout(myComponent, ic.toLong()) })
- PasswordItem(R.string.pwd_history,R.string.pwd_history_desc,R.string.pwd_history_textfield,true,
- {myDpm.getPasswordHistoryLength(null).toString()},{ic -> myDpm.setPasswordHistoryLength(myComponent, ic.toInt()) })
- PasswordItem(R.string.max_time_to_lock,R.string.max_time_to_lock_desc,R.string.time_unit_ms,true,
- {myDpm.getMaximumTimeToLock(myComponent).toString()},{ic -> myDpm.setMaximumTimeToLock(myComponent,ic.toLong())})
-
- if(VERSION.SDK_INT>=31){
- Column(modifier = sections()) {
- 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
- }
- Text(text = stringResource(R.string.required_password_complexity), style = typography.titleLarge,color = titleColor)
- 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},
- if(isWear){
- colorScheme.error}else{
- colorScheme.onBackground})
- Text(text = stringResource(R.string.password_ordered_desc), modifier = Modifier.padding(vertical = 3.dp), style = bodyTextStyle)
- Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
- Button(
- onClick = {
- myDpm.requiredPasswordComplexity = selectedItem
- Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
- },
- enabled = isDeviceOwner(myDpm)|| isProfileOwner(myDpm),
- modifier = if(isWear){Modifier.fillMaxWidth()}else{Modifier.fillMaxWidth(0.4F)}
- ) {
- Text(text = stringResource(R.string.apply))
- }
- if(!isWear){
- Button(
- onClick = {myContext.startActivity(Intent(ACTION_SET_NEW_PASSWORD))},
- modifier = Modifier.fillMaxWidth(0.95F)
- ){
- Text(stringResource(R.string.require_set_new_password))
- }
- }
- }
- if(isWear){
- Button(
- onClick = {myContext.startActivity(Intent(ACTION_SET_NEW_PASSWORD))},
- modifier = Modifier.fillMaxWidth()
- ){
- Text(stringResource(R.string.require_set_new_password))
- }
- }
- }
- }
-
- Column(modifier = sections()){
- 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()
- }
- Text(text = stringResource(R.string.keyguard_disabled_features), style = typography.titleLarge)
- 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}) }
- }
- }
- 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))
- }
- }
-
- var passwordQualityExpand by remember{mutableStateOf(VERSION.SDK_INT < 31)}
- var launchScrollDown by remember{mutableStateOf(false)}
- LaunchedEffect(launchScrollDown){
- if(launchScrollDown){
- delay(10)
- scrollState.animateScrollTo((scrollState.value+myContext.resources.displayMetrics.heightPixels*0.4).toInt(), scrollAnim())
- launchScrollDown=false
- }
- }
- Column(modifier = sections(onClick = {passwordQualityExpand=true;launchScrollDown=true}, clickable = !passwordQualityExpand).animateContentSize(animationSpec = scrollAnim())) {
- 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) }
- Text(text = stringResource(R.string.required_password_quality), style = typography.titleLarge,color = titleColor)
- if(passwordQualityExpand){
- Text(text = stringResource(R.string.password_complexity_instead_password_quality), style = bodyTextStyle)
- }
- if(VERSION.SDK_INT>=31){ Text(text = stringResource(R.string.password_quality_deprecated_desc), color = colorScheme.error, style = bodyTextStyle) }
- if(passwordQualityExpand){
- 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})
- Text(text = stringResource(R.string.password_ordered_desc), modifier = Modifier.padding(vertical = 3.dp), style = bodyTextStyle)
- 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))
- }
- if(VERSION.SDK_INT<31){
- Button(onClick = {myContext.startActivity(Intent(ACTION_SET_NEW_PASSWORD))}){
- Text(stringResource(R.string.require_set_new_password))
- }}
- }
- }
- Spacer(Modifier.padding(vertical = 30.dp))
- }
-}
-
-@Composable
-private fun PasswordItem(
- itemName:Int,
- itemDesc:Int,
- textFieldLabel:Int,
- allowZero:Boolean,
- getMethod:()->String,
- setMethod:(ic:String)->Unit
-){
- val myContext = LocalContext.current
- val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- val isWear = sharedPref.getBoolean("isWear",false)
- val focusMgr = LocalFocusManager.current
- Column(modifier = sections()) {
- var inputContent by remember{ mutableStateOf(if(isDeviceOwner(myDpm)){getMethod()}else{""}) }
- var ableToApply by remember{ mutableStateOf(inputContent!=""&&((inputContent=="0"&&allowZero)||inputContent!="0")) }
- Text(text = stringResource(itemName), style = typography.titleLarge,color = colorScheme.onPrimaryContainer)
- Text(text= stringResource(itemDesc),modifier=Modifier.padding(vertical = 2.dp), style = if(!isWear){typography.bodyLarge}else{typography.bodyMedium})
- if(!isWear){Spacer(Modifier.padding(vertical = 2.dp))}
- Row(
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween,
- modifier = Modifier.fillMaxWidth()
- ){
- OutlinedTextField(
- value = inputContent,
- label = { Text(stringResource(textFieldLabel))},
- onValueChange = {
- inputContent = it
- ableToApply = inputContent!=""&&((inputContent=="0"&&allowZero)||inputContent!="0")
- },
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
- enabled = isDeviceOwner(myDpm),
- modifier = Modifier.focusable().fillMaxWidth(),
- trailingIcon = {
- Icon(
- imageVector = Icons.Outlined.Check, contentDescription = "OK",
- modifier = Modifier
- .clip(RoundedCornerShape(50))
- .clickable(onClick = {focusMgr.clearFocus() ; setMethod(inputContent)}, enabled = isDeviceOwner(myDpm)&&ableToApply)
- .padding(2.dp)
- )
- }
- )
- }
- }
-}
-
-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()
- }
-}
diff --git a/app/src/main/java/com/binbin/androidowner/Permissions.kt b/app/src/main/java/com/binbin/androidowner/Permissions.kt
deleted file mode 100644
index be395e2..0000000
--- a/app/src/main/java/com/binbin/androidowner/Permissions.kt
+++ /dev/null
@@ -1,460 +0,0 @@
-package com.binbin.androidowner
-
-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.animation.animateContentSize
-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.filled.KeyboardArrowRight
-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.focus.FocusDirection
-import androidx.compose.ui.focus.FocusManager
-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.compose.ui.unit.sp
-import androidx.core.content.ContextCompat.startActivity
-import androidx.navigation.NavHostController
-
-
-@Composable
-fun DpmPermissions(navCtrl:NavHostController){
- val myContext = LocalContext.current
- val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
- val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
- val isda = myDpm.isAdminActive(myComponent)
- val focusManager = LocalFocusManager.current
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- val isWear = sharedPref.getBoolean("isWear",false)
- val titleColor = colorScheme.onPrimaryContainer
- val bodyTextStyle = if(isWear){typography.bodyMedium}else{typography.bodyLarge}
- var expandCommandBlock by remember{mutableStateOf("")}
- Column(
- modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- Row(
- modifier = sections(onClick = {navCtrl.navigate("ShizukuActivate")}, clickable = true),
- horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically
- ){
- Text(text = "Shizuku", style = typography.titleLarge, color = titleColor, modifier = Modifier.padding(vertical = 2.dp))
- Icon(imageVector = Icons.Default.KeyboardArrowRight,contentDescription = null, tint = colorScheme.onPrimaryContainer)
- }
- if(!myDpm.isAdminActive(myComponent)&&isWear){
- Button(onClick = { activateDeviceAdmin(myContext,myComponent) },modifier = Modifier.padding(horizontal = 3.dp).fillMaxWidth()) {
- Text(stringResource(R.string.activate_device_admin))
- }
- }
- Row(
- modifier = sections(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Column {
- Text(text = "Device Admin", fontSize = if(!isWear){22.sp}else{20.sp},color = titleColor)
- Text(text = stringResource(if(isda){R.string.activated}else{R.string.deactivated}))
- }
- if(!isWear)
- if(isda){
- if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
- Button(
- onClick = {
- myDpm.removeActiveAdmin(myComponent)
- navCtrl.navigateUp()
- }
- ) {
- Text(stringResource(R.string.deactivate))
- }
- }
- }else{
- Button(onClick = { activateDeviceAdmin(myContext,myComponent) }) {
- Text(stringResource(R.string.activate))
- }
- }
- }
- if(!isda&&!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
- SelectionContainer(
- modifier = sections(colorScheme.tertiaryContainer,{expandCommandBlock="admin"},expandCommandBlock!="admin").animateContentSize(animationSpec = scrollAnim())
- ){
- if(expandCommandBlock=="admin"){
- Text(
- text = stringResource(R.string.activate_device_admin_command),
- color = colorScheme.onTertiaryContainer, style = bodyTextStyle
- )
- }else{
- Text(text = stringResource(R.string.touch_to_view_command), style = bodyTextStyle)
- }
- }
- }
-
- if(!isDeviceOwner(myDpm)){
- Row(
- modifier = sections(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Column {
- Text(text = "Profile Owner", fontSize = if(!isWear){22.sp}else{20.sp},color = titleColor)
- Text(stringResource(if(isProfileOwner(myDpm)){R.string.activated}else{R.string.deactivated}))
- }
- if(isProfileOwner(myDpm)&&VERSION.SDK_INT>=24&&!isWear&&!myDpm.isManagedProfile(myComponent)){
- Button(
- onClick = {
- myDpm.clearProfileOwner(myComponent)
- navCtrl.navigateUp()
- }
- ) {
- Text(stringResource(R.string.deactivate))
- }
- }
- }
- }
- if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
- SelectionContainer(
- modifier = sections(colorScheme.tertiaryContainer,{expandCommandBlock="profile"},expandCommandBlock!="profile").animateContentSize(animationSpec = scrollAnim())
- ){
- if(expandCommandBlock=="profile"){
- Text(
- text = stringResource(R.string.activate_profile_owner_command),
- color = colorScheme.onTertiaryContainer, style = bodyTextStyle
- )
- }else{
- Text(text = stringResource(R.string.touch_to_view_command), style = bodyTextStyle)
- }
- }
- }
-
- if(!isProfileOwner(myDpm)){
- Row(
- modifier = sections(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Column {
- Text(text = "Device Owner", fontSize = if(!isWear){22.sp}else{20.sp},color = titleColor)
- Text(stringResource(if(isDeviceOwner(myDpm)){R.string.activated}else{R.string.deactivated}))
- }
- if(isDeviceOwner(myDpm)&&!isWear){
- Button(
- onClick = {
- myDpm.clearDeviceOwnerApp(myContext.packageName)
- navCtrl.navigateUp()
- }
- ) {
- Text(stringResource(R.string.deactivate))
- }
- }
- }
- }
-
- if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
- SelectionContainer(
- modifier = sections(colorScheme.tertiaryContainer,{expandCommandBlock="device"},expandCommandBlock!="device").animateContentSize(animationSpec = scrollAnim())
- ){
- if(expandCommandBlock=="device"){
- Text(
- text = stringResource(R.string.activate_device_owner_command),
- color = colorScheme.onTertiaryContainer, style = bodyTextStyle
- )
- }else{
- Text(text = stringResource(R.string.touch_to_view_command), style = bodyTextStyle)
- }
- }
- }
- if(VERSION.SDK_INT>=30){
- Column(
- modifier = sections()
- ) {
- Text(text = stringResource(R.string.device_info), style = typography.titleLarge,color = titleColor)
- if(VERSION.SDK_INT>=34&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
- val financed = myDpm.isDeviceFinanced
- Text(stringResource(R.string.is_device_financed, financed),style=bodyTextStyle)
- }
- if(VERSION.SDK_INT>=33){
- val dpmRole = myDpm.devicePolicyManagementRoleHolderPackage
- Text(stringResource(R.string.dpmrh, if(dpmRole==null) { stringResource(R.string.none) } else { "" }),style=bodyTextStyle)
- if(dpmRole!=null){
- SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState())){
- Text(text = dpmRole, style = bodyTextStyle, color = colorScheme.onPrimaryContainer)
- }
- }
- }
- val encryptionStatus = mapOf(
- 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_ACTIVE_DEFAULT_KEY to stringResource(R.string.es_active_default_key),
- DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER to stringResource(R.string.es_active_per_user),
- DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING to stringResource(R.string.unknown)
- )
- Text("加密状态:${encryptionStatus[myDpm.storageEncryptionStatus]}",style=bodyTextStyle)
- val adminList = myDpm.activeAdmins
- if(adminList!=null){
- var adminListText = ""
- Text(text = stringResource(R.string.activated_device_admin, adminList.size), style = bodyTextStyle)
- 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, style = bodyTextStyle, color = titleColor)
- }
- }
- }
- }
- if(VERSION.SDK_INT>=31&&(isProfileOwner(myDpm)|| isDeviceOwner(myDpm))){
- Column(modifier = sections()) {
- val specificId = myDpm.enrollmentSpecificId
- Text(text = stringResource(R.string.enrollment_specific_id), style = typography.titleLarge,color = titleColor)
- if(specificId!=""){
- SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState())){ Text(specificId, style = bodyTextStyle, softWrap = false) }
- }else{
- Text(stringResource(R.string.require_set_org_id),style=bodyTextStyle)
- }
- }
- }
-
- if((VERSION.SDK_INT>=26&&isDeviceOwner(myDpm))||(VERSION.SDK_INT>=24&&isProfileOwner(myDpm))){
- Column(modifier = sections()){
- var orgName by remember{mutableStateOf(try{myDpm.getOrganizationName(myComponent).toString()}catch(e:SecurityException){""})}
- Text(text = stringResource(R.string.org_name), style = typography.titleLarge, color = titleColor)
- 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 = {focusManager.clearFocus()})
- )
- Button(
- onClick = {
- focusManager.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))
- }
- }
- }
-
- if(isDeviceOwner(myDpm) || isProfileOwner(myDpm)){
- Column(modifier = sections()) {
- Text(text = stringResource(R.string.account_types_management_disabled), style = typography.titleLarge,color = titleColor)
- Text(stringResource(R.string.developing),style=bodyTextStyle)
- var noManageAccount = myDpm.accountTypesWithManagementDisabled
- var accountlist by remember{ mutableStateOf("") }
- val refreshList = {
- 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 }
- Text(text = if(accountlist==""){stringResource(R.string.none)}else{accountlist}, style = bodyTextStyle)
- var inputText by remember{ mutableStateOf("") }
- OutlinedTextField(
- value = inputText,
- onValueChange = {inputText=it},
- label = {Text(stringResource(R.string.account_types))},
- modifier = Modifier.focusable().fillMaxWidth().padding(bottom = 4.dp),
- keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = {focusManager.clearFocus()})
- )
- Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
- Button(
- onClick={
- focusManager.clearFocus()
- myDpm.setAccountManagementDisabled(myComponent,inputText,true)
- noManageAccount=myDpm.accountTypesWithManagementDisabled
- refreshList()
- },
- modifier = Modifier.fillMaxWidth(0.49f)
- ){
- Text(stringResource(R.string.add))
- }
- Button(
- onClick={focusManager.clearFocus()
- myDpm.setAccountManagementDisabled(myComponent,inputText,false)
- noManageAccount=myDpm.accountTypesWithManagementDisabled
- refreshList()
- },
- modifier = Modifier.fillMaxWidth(0.96F)
- ){
- Text(stringResource(R.string.remove))
- }
- }
- }
- }
-
- if(VERSION.SDK_INT>=24&&isDeviceOwner(myDpm)){
- DeviceOwnerInfo(R.string.owner_lockscr_info,R.string.place_holder,R.string.owner_lockscr_info,focusManager,myContext,
- {myDpm.deviceOwnerLockScreenInfo},{content -> myDpm.setDeviceOwnerLockScreenInfo(myComponent,content)})
- }
- if((isDeviceOwner(myDpm)||isProfileOwner(myDpm))&&VERSION.SDK_INT>=24){
- DeviceOwnerInfo(R.string.support_msg,R.string.support_msg_desc,R.string.message,focusManager,myContext,
- {myDpm.getShortSupportMessage(myComponent)},{content -> myDpm.setShortSupportMessage(myComponent,content)})
- DeviceOwnerInfo(R.string.long_support_msg,R.string.long_support_msg_desc,R.string.message,focusManager,myContext,
- {myDpm.getLongSupportMessage(myComponent)},{content -> myDpm.setLongSupportMessage(myComponent,content)})
- }
-
- if(VERSION.SDK_INT>=28&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
- Column(modifier = sections()){
- var pkg by remember{mutableStateOf("")}
- var cls by remember{mutableStateOf("")}
- Text(text = stringResource(R.string.transform_ownership), style = typography.titleLarge, color = titleColor)
- Text(text = stringResource(R.string.transform_ownership_desc), style = bodyTextStyle)
- OutlinedTextField(
- value = pkg, onValueChange = {pkg = it}, label = {Text(stringResource(R.string.target_package_name))},
- modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.dp),
- keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
- keyboardActions = KeyboardActions(onNext = {focusManager.moveFocus(FocusDirection.Down)})
- )
- OutlinedTextField(
- value = cls, onValueChange = {cls = it}, label = {Text(stringResource(R.string.target_class_name))},
- modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.dp),
- keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = {focusManager.clearFocus()})
- )
- 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().padding(top = 2.dp)
- ) {
- Text(stringResource(R.string.transform))
- }
- }
- }
-
- if(isWear&&(myDpm.isAdminActive(myComponent)||isProfileOwner(myDpm)||isDeviceOwner(myDpm))){
- Column(modifier = sections(), horizontalAlignment = Alignment.CenterHorizontally) {
- Button(
- onClick = {
- myDpm.removeActiveAdmin(myComponent)
- navCtrl.navigateUp()
- },
- colors = ButtonDefaults.buttonColors(contentColor = colorScheme.onError, containerColor = colorScheme.error),
- enabled = myDpm.isAdminActive(myComponent)
- ) {
- Text(stringResource(R.string.deactivate_da))
- }
- if(VERSION.SDK_INT>=24){
- Button(
- onClick = {
- myDpm.clearProfileOwner(myComponent)
- navCtrl.navigateUp()
- },
- colors = ButtonDefaults.buttonColors(contentColor = colorScheme.onError, containerColor = colorScheme.error),
- enabled = isProfileOwner(myDpm)
- ) {
- Text(stringResource(R.string.deactivate_po))
- }
- }
- Button(
- onClick = {
- myDpm.clearDeviceOwnerApp(myContext.packageName)
- navCtrl.navigateUp()
- },
- colors = ButtonDefaults.buttonColors(contentColor = colorScheme.onError, containerColor = colorScheme.error),
- enabled = isDeviceOwner(myDpm)
- ) {
- Text(stringResource(R.string.deactivate_do))
- }
- }
- }
- Spacer(Modifier.padding(vertical = 30.dp))
- }
-}
-
-@Composable
-fun DeviceOwnerInfo(
- name:Int,
- desc:Int,
- textfield:Int,
- fm:FocusManager,
- myContext:Context,
- input:()->CharSequence?,
- output:(content:String?)->Unit
-){
- Column(modifier = sections()) {
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- val isWear = sharedPref.getBoolean("isWear",false)
- Text(text = stringResource(name), style = typography.titleLarge, softWrap = false, color = colorScheme.onPrimaryContainer)
- if(desc!=R.string.place_holder){
- Text(
- text = stringResource(desc),modifier = Modifier.padding(top = 6.dp),
- style = if(!isWear){typography.bodyLarge}else{typography.bodyMedium})
- }
- var inputContent by remember{ mutableStateOf(input()) }
- OutlinedTextField(
- value = if(inputContent!=null){ inputContent.toString() }else{""},
- label = {Text(stringResource(textfield))},
- onValueChange = { inputContent=it },
- modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 4.dp)
- )
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
- Button(
- onClick = {
- output(inputContent.toString())
- inputContent= input()
- fm.clearFocus()
- Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
- },
- modifier = if(isWear){Modifier.fillMaxWidth(0.49F)}else{Modifier.fillMaxWidth(0.6F)}
- ) {
- Text(text = stringResource(R.string.apply))
- }
- Button(
- onClick = {
- output(null)
- inputContent = input()
- fm.clearFocus()
- Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
- },
- modifier = Modifier.fillMaxWidth(0.96F)
- ) {
- Text(text = stringResource(R.string.reset))
- }
- }
- }
-}
-
-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_android_owner_here))
- startActivity(inputContext,intent,null)
- }catch(e:ActivityNotFoundException){
- Toast.makeText(inputContext,inputContext.getString(R.string.unsupported),Toast.LENGTH_SHORT).show()
- }
-}
diff --git a/app/src/main/java/com/binbin/androidowner/SystemUpdatePolicy.kt b/app/src/main/java/com/binbin/androidowner/SystemUpdatePolicy.kt
deleted file mode 100644
index 3d1fcc6..0000000
--- a/app/src/main/java/com/binbin/androidowner/SystemUpdatePolicy.kt
+++ /dev/null
@@ -1,145 +0,0 @@
-package com.binbin.androidowner
-
-import android.app.admin.DevicePolicyManager
-import android.app.admin.SystemUpdateInfo
-import android.app.admin.SystemUpdatePolicy
-import android.content.ComponentName
-import android.content.Context
-import android.os.Build.VERSION
-import android.widget.Toast
-import androidx.activity.ComponentActivity
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.text.KeyboardActions
-import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material3.Button
-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.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 java.util.Date
-
-@Composable
-fun SysUpdatePolicy(){
- val myContext = LocalContext.current
- val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
- val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::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 {
- if(VERSION.SDK_INT>=26&&isDeviceOwner(myDpm)){
- val sysUpdateInfo = myDpm.getPendingSystemUpdate(myComponent)
- Column(modifier = sections()) {
- if(sysUpdateInfo!=null){
- Text(text = "Update first available: ${Date(sysUpdateInfo.receivedTime)}", style = bodyTextStyle)
- Text(text = "Hash code: ${sysUpdateInfo.hashCode()}", 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)
- }
- }
- }
- if(VERSION.SDK_INT>=23){
- Column(modifier = sections()) {
- var selectedPolicy by remember{ mutableStateOf(myDpm.systemUpdatePolicy?.policyType) }
- Text(text = stringResource(R.string.system_update_policy), style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
- RadioButtonItem(stringResource(R.string.system_update_policy_automatic),{selectedPolicy==SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC},{selectedPolicy=SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC})
- RadioButtonItem(stringResource(R.string.system_update_policy_install_windowed),{selectedPolicy==SystemUpdatePolicy.TYPE_INSTALL_WINDOWED},{selectedPolicy=SystemUpdatePolicy.TYPE_INSTALL_WINDOWED})
- RadioButtonItem(stringResource(R.string.system_update_policy_postpone),{selectedPolicy==SystemUpdatePolicy.TYPE_POSTPONE},{selectedPolicy=SystemUpdatePolicy.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))
- Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.Center) {
- 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)
- }
- val policy =
- when(selectedPolicy){
- SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC->SystemUpdatePolicy.createAutomaticInstallPolicy()
- SystemUpdatePolicy.TYPE_INSTALL_WINDOWED->SystemUpdatePolicy.createWindowedInstallPolicy(windowedPolicyStart.toInt(),windowedPolicyEnd.toInt())
- SystemUpdatePolicy.TYPE_POSTPONE->SystemUpdatePolicy.createPostponeInstallPolicy()
- else->null
- }
- Button(
- onClick = {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>=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("安装")
- }
- }
- }
- }*/
- }
-}
diff --git a/app/src/main/java/com/binbin/androidowner/User.kt b/app/src/main/java/com/binbin/androidowner/User.kt
deleted file mode 100644
index 5d22145..0000000
--- a/app/src/main/java/com/binbin/androidowner/User.kt
+++ /dev/null
@@ -1,380 +0,0 @@
-package com.binbin.androidowner
-
-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.UserHandle
-import android.os.UserManager
-import android.provider.MediaStore
-import android.widget.Toast
-import androidx.activity.ComponentActivity
-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.Button
-import androidx.compose.material3.MaterialTheme.colorScheme
-import androidx.compose.material3.MaterialTheme.typography
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextField
-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.text.input.ImeAction
-import androidx.compose.ui.text.input.KeyboardType
-import androidx.compose.ui.unit.dp
-import androidx.core.os.UserManagerCompat
-
-var affiliationID = mutableSetOf()
-@Composable
-fun UserManage() {
- Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
- val myContext = LocalContext.current
- val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
- val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
- val focusMgr = LocalFocusManager.current
- val userManager = myContext.getSystemService(Context.USER_SERVICE) as UserManager
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- val isWear = sharedPref.getBoolean("isWear",false)
- val bodyTextStyle = if(isWear){ typography.bodyMedium}else{ typography.bodyLarge}
- val titleColor = colorScheme.onPrimaryContainer
- Column(modifier = sections()) {
- Text(text = "用户信息", style = typography.titleLarge, color = titleColor)
- Text("用户已解锁:${UserManagerCompat.isUserUnlocked(myContext)}",style = bodyTextStyle)
- if(VERSION.SDK_INT>=24){ Text("支持多用户:${UserManager.supportsMultipleUsers()}",style = bodyTextStyle) }
- if(VERSION.SDK_INT>=23){ Text(text = "系统用户:${userManager.isSystemUser}", style = bodyTextStyle) }
- if(VERSION.SDK_INT>=34){ Text(text = "管理员用户:${userManager.isAdminUser}", style = bodyTextStyle) }
- if(VERSION.SDK_INT>=31){ Text(text = "无头系统用户: ${UserManager.isHeadlessSystemUserMode()}",style = bodyTextStyle) }
- Spacer(Modifier.padding(vertical = if(isWear){2.dp}else{5.dp}))
- if (VERSION.SDK_INT >= 28) {
- val logoutable = myDpm.isLogoutEnabled
- Text(text = "用户可以退出 : $logoutable",style = bodyTextStyle)
- if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)){
- val ephemeralUser = myDpm.isEphemeralUser(myComponent)
- Text(text = "临时用户: $ephemeralUser",style = bodyTextStyle)
- }
- Text(text = "附属用户: ${myDpm.isAffiliatedUser}",style = bodyTextStyle)
- }
- Spacer(Modifier.padding(vertical = if(isWear){2.dp}else{5.dp}))
- Text(text = "当前UserID:${Binder.getCallingUid()/100000}",style = bodyTextStyle)
- Text(text = "当前用户序列号:${userManager.getSerialNumberForUser(android.os.Process.myUserHandle())}",style = bodyTextStyle)
- }
-
- Column(modifier = sections()) {
- Text(text = "用户操作", style = typography.titleLarge,color = titleColor)
- var idInput by remember{ mutableStateOf("") }
- var userHandleById:UserHandle by remember{ mutableStateOf(android.os.Process.myUserHandle()) }
- var useUid by remember{ mutableStateOf(false) }
- TextField(
- 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 ?: android.os.Process.myUserHandle()
- }
- },
- label = {Text(if(useUid){"UID"}else{"序列号"})},
- enabled = isDeviceOwner(myDpm),
- modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 3.dp),
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
- )
- if(VERSION.SDK_INT>=24&&isDeviceOwner(myDpm)){
- CheckBoxItem(text = "使用UID", checked = {useUid}, operation = {idInput=""; useUid = !useUid})
- }
- if(VERSION.SDK_INT>28){
- if(isProfileOwner(myDpm)&&myDpm.isAffiliatedUser){
- Button(
- onClick = {
- val result = myDpm.logoutUser(myComponent)
- Toast.makeText(myContext, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
- },
- enabled = isProfileOwner(myDpm),
- modifier = Modifier.fillMaxWidth()
- ) {
- Text("登出当前用户")
- }
- }
- }
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
- Button(
- onClick = {
- focusMgr.clearFocus()
- if(VERSION.SDK_INT>=28){
- val result = myDpm.startUserInBackground(myComponent,userHandleById)
- Toast.makeText(myContext, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
- }
- },
- enabled = isDeviceOwner(myDpm)&&VERSION.SDK_INT>=28,
- modifier = Modifier.fillMaxWidth(0.49F)
- ){
- Text(if(isWear){"启动"}else{"在后台启动"})
- }
- Button(
- onClick = {
- focusMgr.clearFocus()
- if(myDpm.switchUser(myComponent,userHandleById)){
- Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
- }else{
- Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
- }
- },
- enabled = isDeviceOwner(myDpm),
- modifier = Modifier.fillMaxWidth(0.96F)
- ) {
- Text("切换")
- }
- }
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
- Button(
- onClick = {
- focusMgr.clearFocus()
- try{
- if(VERSION.SDK_INT>=28){
- val result = myDpm.stopUser(myComponent,userHandleById)
- Toast.makeText(myContext, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
- }
- }catch(e:IllegalArgumentException){
- Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
- }
- },
- enabled = isDeviceOwner(myDpm)&&VERSION.SDK_INT>=28,
- modifier = Modifier.fillMaxWidth(0.49F)
- ) {
- Text("停止")
- }
- Button(
- onClick = {
- focusMgr.clearFocus()
- if(myDpm.removeUser(myComponent,userHandleById)){
- Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
- idInput=""
- }else{
- Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
- }
- },
- enabled = isDeviceOwner(myDpm),
- modifier = Modifier.fillMaxWidth(0.96F)
- ) {
- Text("移除")
- }
- }
- if(VERSION.SDK_INT<28){
- Text(text = "停止用户需API28", style = bodyTextStyle)
- }
- }
-
- if(VERSION.SDK_INT>=24){
- Column(modifier = sections()) {
- var userName by remember{ mutableStateOf("") }
- Text(text = "创建用户", style = typography.titleLarge, color = titleColor)
- TextField(
- value = userName,
- onValueChange = {userName=it},
- label = {Text("用户名")},
- modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 4.dp),
- keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
- )
- var selectedFlag by remember{ mutableIntStateOf(0) }
- RadioButtonItem("无",{selectedFlag==0},{selectedFlag=0})
- RadioButtonItem("跳过创建用户向导",{selectedFlag==DevicePolicyManager.SKIP_SETUP_WIZARD},{selectedFlag=DevicePolicyManager.SKIP_SETUP_WIZARD})
- if(VERSION.SDK_INT>=28){
- RadioButtonItem("临时用户",{selectedFlag==DevicePolicyManager.MAKE_USER_EPHEMERAL},{selectedFlag=DevicePolicyManager.MAKE_USER_EPHEMERAL})
- RadioButtonItem("启用所有系统应用",{selectedFlag==DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED},{selectedFlag=DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED})
- }
- var newUserHandle: UserHandle? by remember{ mutableStateOf(null) }
- 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("创建(Owner)")
- }
- if(newUserHandle!=null){ Text(text = "新用户的序列号:${userManager.getSerialNumberForUser(newUserHandle)}", style = bodyTextStyle) }
- }
- }
- if(VERSION.SDK_INT>=26&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
- Column(modifier = sections()){
- var input by remember{mutableStateOf("")}
- var list by remember{mutableStateOf("")}
- val refresh = {
- list = ""
- var count = affiliationID.size
- for(item in affiliationID){ count-=1; list+=item; if(count>0){list+="\n"} }
- }
- var inited by remember{mutableStateOf(false)}
- if(!inited){affiliationID = myDpm.getAffiliationIds(myComponent);refresh();inited=true}
- Text(text = "附属用户ID", style = typography.titleLarge, color = titleColor)
- TextField(
- 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()})
- )
- if(list!=""){
- SelectionContainer {
- Text(text = list, style = bodyTextStyle)
- }
- }else{
- Text(text = "无", style = bodyTextStyle)
- }
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
- Button(
- onClick = { affiliationID.add(input); refresh() },
- modifier = Modifier.fillMaxWidth(0.49F)
- ){
- Text("添加")
- }
- Button(
- onClick = { affiliationID.remove(input); refresh() },
- modifier = Modifier.fillMaxWidth(0.96F)
- ){
- Text("移除")
- }
- }
- Button(
- onClick = {
- if("" in affiliationID) {
- Toast.makeText(myContext, "有空字符串", Toast.LENGTH_SHORT).show()
- }else if(affiliationID.isEmpty()){
- Toast.makeText(myContext, "不能为空", Toast.LENGTH_SHORT).show()
- }else{
- myDpm.setAffiliationIds(myComponent, affiliationID)
- affiliationID = myDpm.getAffiliationIds(myComponent)
- refresh()
- Toast.makeText(myContext,"成功",Toast.LENGTH_SHORT).show()
- }
- },
- modifier = Modifier.fillMaxWidth()
- ) {
- Text("应用")
- }
- Text(text = "如果多用户,附属用户ID相同时可以让其他用户附属于主用户", style = bodyTextStyle)
- }
- }
-
- UserSessionMessage("用户名", "用户名", true, {null}) {msg-> myDpm.setProfileName(myComponent, msg.toString())}
-
- if(VERSION.SDK_INT>=23&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
- Column(modifier = sections()){
- var getContent by remember{mutableStateOf(false)}
- Text(text = "用户图标", style = typography.titleLarge, color = titleColor)
- Text(text = "尽量选择正方形的图片,以免产生问题", style = bodyTextStyle)
- CheckBoxItem("使用文件选择器而不是相册",{getContent},{getContent=!getContent})
- 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("选择图片...")
- }
- Button(
- onClick = {
- if(userIconUri!=null){
- uriToStream(myContext, userIconUri){stream ->
- val bitmap = BitmapFactory.decodeStream(stream)
- myDpm.setUserIcon(myComponent,bitmap)
- Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
- }
- }else{
- Toast.makeText(myContext, "请先选择图片", Toast.LENGTH_SHORT).show()
- }
- },
- modifier = Modifier.fillMaxWidth()
- ) {
- Text("应用")
- }
- }
- }
-
- if(VERSION.SDK_INT>=28){
- UserSessionMessage("用户会话开始消息", "消息", false, {myDpm.getStartUserSessionMessage(myComponent)}) {msg-> myDpm.setStartUserSessionMessage(myComponent, msg)}
- UserSessionMessage("用户会话结束消息", "消息", false, {myDpm.getEndUserSessionMessage(myComponent)}) {msg-> myDpm.setEndUserSessionMessage(myComponent, msg)}
- }
- Spacer(Modifier.padding(vertical = 30.dp))
- }
-}
-
-@Composable
-private fun UserSessionMessage(text:String, textField:String, profileOwner:Boolean, get: ()->CharSequence?, setMsg:(msg: CharSequence?)->Unit){
- Column(
- modifier = sections()
- ) {
- val myContext = LocalContext.current
- val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
- val focusMgr = LocalFocusManager.current
- var msg by remember{ mutableStateOf(if(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&profileOwner)){ if(get()==null){""}else{get().toString()} }else{""}) }
- val sharedPref = myContext.getSharedPreferences("data", Context.MODE_PRIVATE)
- val isWear = sharedPref.getBoolean("isWear",false)
- Text(text = text, style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
- TextField(
- value = msg,
- onValueChange = {msg=it},
- label = {Text(textField)},
- keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
- modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 6.dp),
- enabled = isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&profileOwner)
- )
- Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {
- Button(
- onClick = {
- focusMgr.clearFocus()
- setMsg(msg)
- msg = if(get()==null){""}else{get().toString()}
- Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
- },
- enabled = isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&profileOwner),
- modifier = Modifier.fillMaxWidth(if(isWear){0.49F}else{0.65F})
- ) {
- Text("应用")
- }
- Button(
- onClick = {
- focusMgr.clearFocus()
- setMsg(null)
- msg = get()?.toString() ?: ""
- Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
- },
- enabled = isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&profileOwner),
- modifier = Modifier.fillMaxWidth(0.96F)
- ) {
- Text("默认")
- }
- }
- }
-}
-
-private fun userOperationResultCode(result:Int): String {
- return when(result){
- UserManager.USER_OPERATION_SUCCESS->"成功"
- UserManager.USER_OPERATION_ERROR_UNKNOWN->"未知结果(失败)"
- UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE->"失败:受管理的资料"
- UserManager.USER_OPERATION_ERROR_CURRENT_USER->"失败:当前用户"
- else->"未知"
- }
-}
diff --git a/app/src/main/java/com/binbin/androidowner/Utils.kt b/app/src/main/java/com/binbin/androidowner/Utils.kt
new file mode 100644
index 0000000..16f8975
--- /dev/null
+++ b/app/src/main/java/com/binbin/androidowner/Utils.kt
@@ -0,0 +1,26 @@
+package com.binbin.androidowner
+
+import android.content.Context
+import android.net.Uri
+import android.widget.Toast
+import java.io.FileNotFoundException
+import java.io.IOException
+import java.io.InputStream
+
+
+fun uriToStream(
+ context: Context,
+ uri: Uri?,
+ operation:(stream: InputStream)->Unit
+){
+ if(uri!=null){
+ try{
+ val stream = context.contentResolver.openInputStream(uri)
+ if(stream!=null) { operation(stream) }
+ else{ Toast.makeText(context, "空的流", Toast.LENGTH_SHORT).show() }
+ stream?.close()
+ }
+ catch(e: FileNotFoundException){ Toast.makeText(context, "文件不存在", Toast.LENGTH_SHORT).show() }
+ catch(e: IOException){ Toast.makeText(context, "IO异常", Toast.LENGTH_SHORT).show() }
+ }else{ Toast.makeText(context, "空URI", Toast.LENGTH_SHORT).show() }
+}
diff --git a/app/src/main/java/com/binbin/androidowner/Receiver.kt b/app/src/main/java/com/binbin/androidowner/dpm/AdminReceiver.kt
similarity index 99%
rename from app/src/main/java/com/binbin/androidowner/Receiver.kt
rename to app/src/main/java/com/binbin/androidowner/dpm/AdminReceiver.kt
index 97c90ab..3963060 100644
--- a/app/src/main/java/com/binbin/androidowner/Receiver.kt
+++ b/app/src/main/java/com/binbin/androidowner/dpm/AdminReceiver.kt
@@ -1,4 +1,4 @@
-package com.binbin.androidowner
+package com.binbin.androidowner.dpm
import android.annotation.SuppressLint
import android.app.admin.DeviceAdminReceiver
diff --git a/app/src/main/java/com/binbin/androidowner/dpm/ApplicationManage.kt b/app/src/main/java/com/binbin/androidowner/dpm/ApplicationManage.kt
new file mode 100644
index 0000000..14ffd3e
--- /dev/null
+++ b/app/src/main/java/com/binbin/androidowner/dpm/ApplicationManage.kt
@@ -0,0 +1,798 @@
+package com.binbin.androidowner.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.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.material3.*
+import androidx.compose.material3.MaterialTheme.colorScheme
+import androidx.compose.material3.MaterialTheme.typography
+import androidx.compose.runtime.*
+import androidx.compose.runtime.saveable.rememberSaveable
+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.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.core.content.ContextCompat.startActivity
+import com.binbin.androidowner.R
+import com.binbin.androidowner.ui.RadioButtonItem
+import com.binbin.androidowner.uriToStream
+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(){
+ val myContext = LocalContext.current
+ val focusMgr = LocalFocusManager.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ var pkgName by rememberSaveable{ mutableStateOf("") }
+ val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
+ val isWear = sharedPref.getBoolean("isWear",false)
+ val bodyTextStyle = if(isWear){ typography.bodyMedium }else{ typography.bodyLarge }
+ val titleColor = colorScheme.onPrimaryContainer
+ Column{
+ TextField(
+ value = pkgName,
+ onValueChange = { pkgName = it },
+ label = { Text(stringResource(R.string.package_name)) },
+ modifier = Modifier.fillMaxWidth().padding(horizontal = 4.dp),
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
+ keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
+ )
+ Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
+ if(VERSION.SDK_INT>=24&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
+ Text(text = stringResource(R.string.scope_is_work_profile), style = bodyTextStyle, textAlign = TextAlign.Center,modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp))
+ }
+
+ Button(
+ onClick = {
+ val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ intent.setData(Uri.parse("package:$pkgName"))
+ startActivity(myContext,intent,null)
+ },
+ modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)
+ ){
+ Text(stringResource(R.string.app_info))
+ }
+
+ if(VERSION.SDK_INT>=24&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
+ AppManageItem(
+ R.string.suspend,R.string.place_holder,
+ {try{ myDpm.isPackageSuspended(myComponent,pkgName) }
+ catch(e:NameNotFoundException){ false }
+ catch(e:IllegalArgumentException){ false }}
+ ) { b -> myDpm.setPackagesSuspended(myComponent, arrayOf(pkgName), b) }
+ }
+ if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
+ AppManageItem(R.string.hide,R.string.isapphidden_desc, {myDpm.isApplicationHidden(myComponent,pkgName)}) {b-> myDpm.setApplicationHidden(myComponent, pkgName, b)}
+ }
+ if(VERSION.SDK_INT>=24&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
+ AppManageItem(
+ R.string.always_on_vpn,R.string.place_holder,{pkgName == myDpm.getAlwaysOnVpnPackage(myComponent)}) {b->
+ try {
+ myDpm.setAlwaysOnVpnPackage(myComponent, pkgName, b)
+ } catch(e: java.lang.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)){
+ Column{
+ var state by remember{mutableStateOf(myDpm.isUninstallBlocked(myComponent,pkgName))}
+ Text(text = stringResource(R.string.block_uninstall), style = typography.titleLarge, color = titleColor)
+ Text(stringResource(R.string.current_state, stringResource(if(state){R.string.enabled}else{R.string.disabled})))
+ Text(text = stringResource(R.string.sometimes_get_wrong_block_uninstall_state), style = bodyTextStyle)
+ 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))
+ }
+ }
+ }
+ }
+
+ if(VERSION.SDK_INT>=30&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
+ UserCtrlDisabledPkg(pkgName)
+ }
+
+ if(VERSION.SDK_INT>=23&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
+ PermissionManage(pkgName)
+ }
+
+ if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
+ CrossProfilePkg(pkgName)
+ }
+
+ if(isProfileOwner(myDpm)){
+ CrossProfileWidget(pkgName)
+ }
+
+ if(VERSION.SDK_INT>=34&&isDeviceOwner(myDpm)){
+ CredentialManagePolicy(pkgName)
+ }
+
+ if(isProfileOwner(myDpm)||isDeviceOwner(myDpm)){
+ PermittedAccessibility(pkgName)
+ }
+
+ if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
+ PermittedIME(pkgName)
+ }
+
+ if(VERSION.SDK_INT>=28&&isDeviceOwner(myDpm)){
+ KeepUninstalledApp(pkgName)
+ }
+
+ if(VERSION.SDK_INT>=28){
+ 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))
+ }
+ }
+
+ if(VERSION.SDK_INT>=34){
+ 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))
+ }
+ }
+
+ UninstallApp(pkgName)
+
+ InstallApp()
+
+ Spacer(Modifier.padding(30.dp))
+ }
+ }
+}
+
+@Composable
+private fun AppManageItem(
+ itemName:Int,
+ itemDesc:Int,
+ getMethod:()->Boolean,
+ setMethod:(b:Boolean)->Unit
+){
+ val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
+ Row(
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ var enabled by remember{mutableStateOf(getMethod())}
+ enabled = getMethod()
+ Column(modifier = if(sharedPref.getBoolean("isWear",false)){Modifier.fillMaxWidth(0.65F)}else{Modifier}){
+ Text(text = stringResource(itemName), style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
+ if(itemDesc!=R.string.place_holder){ Text(stringResource(itemDesc)) }
+ }
+ Switch(
+ checked = enabled,
+ onCheckedChange = { setMethod(!enabled); enabled=getMethod() }
+ )
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun UserCtrlDisabledPkg(pkgName:String){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ Column{
+ var pkgList = myDpm.getUserControlDisabledPackages(myComponent)
+ var listText by remember{mutableStateOf("")}
+ val refresh = {
+ pkgList = myDpm.getUserControlDisabledPackages(myComponent)
+ listText = ""
+ var count = pkgList.size
+ for(pkg in pkgList){ count-=1; listText+=pkg; if(count>0){listText+="\n"} }
+ }
+ var inited by remember{mutableStateOf(false)}
+ if(!inited){refresh();inited=true}
+ Text(text = stringResource(R.string.ucd), style = typography.titleLarge)
+ Text(text = stringResource(R.string.ucd_desc))
+ 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})
+ }
+ 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))
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun PermissionManage(pkgName: String){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ 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)
+ )
+ Column{
+ var inputPermission by remember{mutableStateOf("android.permission.")}
+ var currentState by remember{mutableStateOf(grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)])}
+ Text(text = stringResource(R.string.permission_manage), style = typography.titleLarge)
+ OutlinedTextField(
+ value = inputPermission,
+ label = { Text(stringResource(R.string.permission))},
+ onValueChange = {inputPermission = it},
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
+ keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
+ modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.dp)
+ )
+ Text(stringResource(R.string.current_state, currentState?:stringResource(R.string.unknown)))
+ 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))
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun CrossProfilePkg(pkgName: String){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ Column{
+ Text(text = stringResource(R.string.cross_profile_package), style = typography.titleLarge)
+ var list by remember{mutableStateOf("")}
+ val refresh = {
+ crossProfilePkg = myDpm.getCrossProfilePackages(myComponent)
+ list = ""
+ var count = crossProfilePkg.size
+ for(each in crossProfilePkg){ count-=1; list+=each; if(count>0){list+="\n"} }
+ }
+ var inited by remember{mutableStateOf(false)}
+ if(!inited){refresh();inited=true}
+ SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
+ 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))
+ }
+ }
+ }
+}
+
+@Composable
+fun CrossProfileWidget(pkgName: String){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ Column{
+ var pkgList: MutableList
+ var list by remember{mutableStateOf("")}
+ val refresh = {
+ pkgList = myDpm.getCrossProfileWidgetProviders(myComponent)
+ list = ""
+ var count = pkgList.size
+ for(each in pkgList){ count-=1; list+=each; if(count>0){list+="\n"}}
+ }
+ var inited by remember{mutableStateOf(false)}
+ if(!inited){refresh();inited=true}
+ Text(text = stringResource(R.string.cross_profile_widget), style = typography.titleLarge)
+ Text(text = stringResource(R.string.cross_profile_widget_desc))
+ SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
+ Text(text = if(list==""){stringResource(R.string.none)}else{list})
+ }
+ 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))
+ }
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+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()
+ }
+ val refreshText = {
+ credentialListText = ""
+ var count = credentialList.size
+ for(item in credentialList){ count-=1; credentialListText+=item; if(count>0){credentialListText+="\n"} }
+ }
+ var inited by remember{mutableStateOf(false)}
+ if(!inited){refreshPolicy(); refreshText(); inited = true}
+ Column{
+ Text(text = stringResource(R.string.credential_manage_policy), style = typography.titleLarge)
+ 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})
+ AnimatedVisibility(policyType!=-1) {
+ Column {
+ Text("应用列表")
+ SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
+ Text(text = if(credentialListText!=""){ credentialListText }else{ stringResource(R.string.none) })
+ }
+ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
+ Button(
+ onClick = {
+ if(pkgName!=""){credentialList.add(pkgName)}
+ refreshText()
+ },
+ modifier = Modifier.fillMaxWidth(0.49F)
+ ) {
+ Text(stringResource(R.string.add))
+ }
+ Button(
+ onClick = {
+ if(pkgName!=""){credentialList.remove(pkgName)}
+ refreshText()
+ },
+ 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()
+ refreshText()
+ }
+ },
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(stringResource(R.string.apply))
+ }
+ }
+}
+
+@Composable
+fun PermittedAccessibility(pkgName: String){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column {
+ Text(text = stringResource(R.string.permitted_accessibility_app), style = typography.titleLarge)
+ var listText by remember{ mutableStateOf("") }
+ val refreshList = {
+ listText = ""
+ var count = permittedAccessibility.size
+ for(eachAccessibility in permittedAccessibility){ count-=1; listText+=eachAccessibility; if(count>0){listText+="\n"} }
+ }
+ var inited by remember{mutableStateOf(false)}
+ if(!inited){
+ val getList = myDpm.getPermittedAccessibilityServices(myComponent)
+ if(getList!=null){ permittedAccessibility = getList }
+ refreshList(); inited=true
+ }
+ SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
+ Text(text = if(listText==""){stringResource(R.string.none)}else{listText})
+ }
+ Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
+ Button(
+ onClick = { permittedAccessibility.add(pkgName); refreshList()},
+ modifier = Modifier.fillMaxWidth(0.49F)
+ ) {
+ Text(stringResource(R.string.add))
+ }
+ Button(
+ onClick = { permittedAccessibility.remove(pkgName); refreshList() },
+ 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 }
+ refreshList()
+ },
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(text = stringResource(R.string.apply))
+ }
+ }
+}
+
+@Composable
+fun PermittedIME(pkgName: String){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column {
+ Text(text = stringResource(R.string.permitted_ime), style = typography.titleLarge)
+ var imeListText by remember{ mutableStateOf("") }
+ val refreshList = {
+ imeListText = ""
+ for(eachIme in permittedIme){ imeListText += "$eachIme \n" }
+ }
+ var inited by remember{mutableStateOf(false)}
+ if(!inited){
+ val getList = myDpm.getPermittedInputMethods(myComponent)
+ if(getList!=null){ permittedIme = getList }
+ refreshList();inited=true
+ }
+ SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
+ Text(text = if(imeListText==""){stringResource(R.string.none)}else{imeListText})
+ }
+ Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
+ Button(
+ onClick = { permittedIme.add(pkgName); refreshList() },
+ modifier = Modifier.fillMaxWidth(0.49F)
+ ) {
+ Text(stringResource(R.string.add))
+ }
+ Button(
+ onClick = { permittedIme.remove(pkgName); refreshList()},
+ 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 }
+ refreshList()
+ },
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(stringResource(R.string.apply))
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun KeepUninstalledApp(pkgName: String){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ Text(text = stringResource(R.string.keep_uninstalled_pkgs), style = typography.titleLarge)
+ var listText by remember{mutableStateOf("")}
+ val refresh = {
+ listText = ""
+ var count = keepUninstallPkg.size
+ for(each in keepUninstallPkg){ count-=1; listText+=each; if(count>0){listText+="\n"} }
+ }
+ var inited by remember{mutableStateOf(false)}
+ if(!inited){
+ val getList = myDpm.getKeepUninstalledPackages(myComponent)
+ if(getList!=null){ keepUninstallPkg = getList }
+ refresh(); inited=true
+ }
+ SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())){
+ Text(text = if(listText==""){stringResource(R.string.none)}else{listText})
+ }
+ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
+ Button(
+ onClick = {
+ keepUninstallPkg.add(pkgName)
+ refresh()
+ },
+ modifier = Modifier.fillMaxWidth(0.49F)
+ ){
+ Text(stringResource(R.string.add))
+ }
+ Button(
+ onClick = {
+ keepUninstallPkg.remove(pkgName)
+ refresh()
+ },
+ 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 }
+ Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
+ },
+ modifier = Modifier.fillMaxWidth()
+ ){
+ Text(stringResource(R.string.apply))
+ }
+ }
+}
+
+@Composable
+fun UninstallApp(pkgName: String){
+ val myContext = LocalContext.current
+ Column{
+ Text(text = stringResource(R.string.uninstall_app), style = typography.titleLarge)
+ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
+ 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(0.49F)
+ ) {
+ 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(0.96F)
+ ) {
+ Text(stringResource(R.string.request_uninstall))
+ }
+ }
+ }
+}
+
+@Composable
+fun InstallApp(){
+ val myContext = LocalContext.current
+ val focusMgr = LocalFocusManager.current
+ Column{
+ Text(text = stringResource(R.string.install_app), style = typography.titleLarge)
+ 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(500); selected = apkUri!=null}}
+ AnimatedVisibility(selected) {
+ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
+ Button(
+ onClick = { uriToStream(myContext, apkUri){stream -> installPackage(myContext,stream)} },
+ modifier = Modifier.fillMaxWidth(0.49F)
+ ) {
+ 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(0.96F)
+ ) {
+ Text(stringResource(R.string.request_install))
+ }
+ }
+ }
+ }
+}
+
+@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)
+}
diff --git a/app/src/main/java/com/binbin/androidowner/dpm/DPM.kt b/app/src/main/java/com/binbin/androidowner/dpm/DPM.kt
new file mode 100644
index 0000000..8ce03d7
--- /dev/null
+++ b/app/src/main/java/com/binbin/androidowner/dpm/DPM.kt
@@ -0,0 +1,26 @@
+package com.binbin.androidowner.dpm
+
+import android.app.admin.DevicePolicyManager
+import android.content.Intent
+import android.net.Uri
+import androidx.activity.result.ActivityResultLauncher
+
+
+lateinit var getCaCert: ActivityResultLauncher
+lateinit var createUser: ActivityResultLauncher
+lateinit var createManagedProfile: ActivityResultLauncher
+lateinit var getApk: ActivityResultLauncher
+lateinit var getUserIcon: ActivityResultLauncher
+
+var userIconUri: Uri? = null
+var apkUri: Uri? = null
+var caCert = byteArrayOf()
+
+fun isDeviceOwner(dpm: DevicePolicyManager): Boolean {
+ return dpm.isDeviceOwnerApp("com.binbin.androidowner")
+}
+
+fun isProfileOwner(dpm: DevicePolicyManager): Boolean {
+ return dpm.isProfileOwnerApp("com.binbin.androidowner")
+}
+
diff --git a/app/src/main/java/com/binbin/androidowner/dpm/ManagedProfile.kt b/app/src/main/java/com/binbin/androidowner/dpm/ManagedProfile.kt
new file mode 100644
index 0000000..2fd2b33
--- /dev/null
+++ b/app/src/main/java/com/binbin/androidowner/dpm/ManagedProfile.kt
@@ -0,0 +1,282 @@
+package com.binbin.androidowner.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.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.Button
+import androidx.compose.material3.MaterialTheme.colorScheme
+import androidx.compose.material3.MaterialTheme.typography
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Switch
+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 androidx.navigation.NavHostController
+import com.binbin.androidowner.R
+import com.binbin.androidowner.ui.CheckBoxItem
+
+@Composable
+fun ManagedProfile() {
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
+ val isWear = sharedPref.getBoolean("isWear",false)
+ val bodyTextStyle = if(isWear){ typography.bodyMedium}else{ typography.bodyLarge}
+ val titleColor = colorScheme.onPrimaryContainer
+ Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())){
+ Column{
+ Text(text = stringResource(R.string.info), style = typography.titleLarge, color = titleColor)
+ if(VERSION.SDK_INT>=24){
+ if(isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
+ Text(text = stringResource(R.string.is_already_work_profile))
+ }else{
+ Text(text = stringResource(R.string.able_to_create_work_profile, myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)), style = bodyTextStyle)
+ if(isDeviceOwner(myDpm)){
+ Text(text = stringResource(R.string.device_owner_cannot_create_work_profile), style = bodyTextStyle)
+ }
+ }
+ }
+ if(VERSION.SDK_INT>=30){
+ Text(text = stringResource(R.string.is_org_owned_profile, myDpm.isOrganizationOwnedDeviceWithManagedProfile), style = bodyTextStyle)
+ }
+ }
+
+ if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)&&!myDpm.isOrganizationOwnedDeviceWithManagedProfile){
+ OrgOwnedProfile()
+
+ }
+ if(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE))){
+ CreateWorkProfile()
+ }
+
+ if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile){
+ SuspendPersonalApp()
+ }
+
+ if(isProfileOwner(myDpm)&&(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isManagedProfile(myComponent)))){
+ IntentFilter()
+ }
+
+ if(VERSION.SDK_INT>=31&&(isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent))){
+ OrgID()
+ }
+
+ Spacer(Modifier.padding(vertical = 30.dp))
+ }
+}
+
+@Composable
+fun CreateWorkProfile(){
+ val myContext = LocalContext.current
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ Column{
+ Text(text = stringResource(R.string.work_profile), style = typography.titleLarge)
+ var skipEncrypt by remember{mutableStateOf(false)}
+ if(VERSION.SDK_INT>=24){CheckBoxItem(stringResource(R.string.skip_encryption),{skipEncrypt},{skipEncrypt=!skipEncrypt})}
+ 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,"com.binbin.androidowner")
+ }
+ 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))
+ }
+ }
+}
+
+@Composable
+fun OrgOwnedProfile(){
+ Column{
+ Text(text = stringResource(R.string.org_owned_work_profile), color = colorScheme.onTertiaryContainer, style = typography.titleLarge)
+ SelectionContainer {
+ Text(text = "使用ADB执行以下命令,或者使用Shizuku")
+ Text(
+ text = stringResource(R.string.activate_org_profile_command, Binder.getCallingUid()/100000),
+ color = colorScheme.onTertiaryContainer
+ )
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun OrgID(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val focusMgr = LocalFocusManager.current
+ Column{
+ var orgId by remember{mutableStateOf("")}
+ Text(text = stringResource(R.string.org_id), style = typography.titleLarge)
+ 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().padding(vertical = 2.dp)
+ )
+ AnimatedVisibility(orgId.length !in 6..64) {
+ Text(text = stringResource(R.string.length_6_to_64))
+ }
+ 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))
+ }
+ Text(text = stringResource(R.string.get_specific_id_after_set_org_id))
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun SuspendPersonalApp(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ Row(horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically){
+ var suspended by remember{mutableStateOf(false)}
+ suspended = myDpm.getPersonalAppsSuspendedReasons(myComponent)!=PERSONAL_APPS_NOT_SUSPENDED
+ Text(text = stringResource(R.string.suspend_personal_app), style = typography.titleLarge)
+ Switch(
+ checked = suspended,
+ onCheckedChange ={
+ myDpm.setPersonalAppsSuspended(myComponent,!suspended)
+ suspended = myDpm.getPersonalAppsSuspendedReasons(myComponent)!=PERSONAL_APPS_NOT_SUSPENDED
+ }
+ )
+ }
+ var time by remember{mutableStateOf("")}
+ time = myDpm.getManagedProfileMaximumTimeOff(myComponent).toString()
+ 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
+fun IntentFilter(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ var action by remember{mutableStateOf("")}
+ Text(text = stringResource(R.string.intent_filter), style = typography.titleLarge)
+ 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().padding(vertical = 2.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))
+ }
+ Button(
+ onClick = {
+ myDpm.clearCrossProfileIntentFilters(myComponent)
+ myDpm.addCrossProfileIntentFilter(myComponent, IntentFilter("com.binbin.androidowner.MAIN_ACTION"), FLAG_MANAGED_CAN_ACCESS_PARENT)
+ myDpm.addCrossProfileIntentFilter(myComponent, IntentFilter("com.binbin.androidowner.MAIN_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.clear_cross_profile_filters))
+ }
+ }
+}
+
+@Composable
+fun ActivateManagedProfile(navCtrl: NavHostController){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val sharedPref = myContext.getSharedPreferences("data", Context.MODE_PRIVATE)
+ myDpm.addCrossProfileIntentFilter(myComponent, IntentFilter("com.binbin.androidowner.MAIN_ACTION"), FLAG_MANAGED_CAN_ACCESS_PARENT)
+ myDpm.addCrossProfileIntentFilter(myComponent, IntentFilter("com.binbin.androidowner.MAIN_ACTION"), FLAG_PARENT_CAN_ACCESS_MANAGED)
+ Column(modifier = Modifier.verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally){
+ Text(text = stringResource(R.string.activate_managed_profile), style = typography.titleLarge)
+ Text(text = stringResource(R.string.activate_managed_profile_desc))
+ Button(
+ onClick = {
+ myDpm.setProfileEnabled(myComponent)
+ navCtrl.popBackStack("HomePage",false)
+ sharedPref.edit().putBoolean("ManagedProfileActivated",true).apply()
+ Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
+ },
+ modifier = Modifier.fillMaxWidth().padding(8.dp)
+ ) {
+ Text(stringResource(R.string.activate))
+ }
+ }
+}
diff --git a/app/src/main/java/com/binbin/androidowner/dpm/Network.kt b/app/src/main/java/com/binbin/androidowner/dpm/Network.kt
new file mode 100644
index 0000000..badce23
--- /dev/null
+++ b/app/src/main/java/com/binbin/androidowner/dpm/Network.kt
@@ -0,0 +1,741 @@
+package com.binbin.androidowner.dpm
+
+import android.annotation.SuppressLint
+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.content.Context
+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.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.material3.Button
+import androidx.compose.material3.MaterialTheme.colorScheme
+import androidx.compose.material3.MaterialTheme.typography
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Switch
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+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.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.core.net.toUri
+import com.binbin.androidowner.R
+import com.binbin.androidowner.ui.RadioButtonItem
+
+var ssidSet = mutableSetOf()
+@Composable
+fun Network(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
+ val isWear = sharedPref.getBoolean("isWear",false)
+ val bodyTextStyle = if(isWear){ typography.bodyMedium }else{ typography.bodyLarge }
+ Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())){
+
+ if(VERSION.SDK_INT>=24){
+ val wifimac = try { myDpm.getWifiMacAddress(myComponent).toString() }catch(e:SecurityException){ "没有权限" }
+ Text(text = "WiFi MAC: $wifimac",modifier=Modifier.fillMaxWidth(), textAlign = TextAlign.Center,style=bodyTextStyle)
+ }
+
+ if(VERSION.SDK_INT>=33&&isDeviceOwner(myDpm)){
+ DeviceCtrlItem(
+ R.string.preferential_network_service,R.string.developing,R.drawable.globe_fill0,
+ {myDpm.isPreferentialNetworkServiceEnabled},{b -> myDpm.isPreferentialNetworkServiceEnabled = b}
+ )
+ }
+ if(VERSION.SDK_INT>=30&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
+ DeviceCtrlItem(R.string.wifi_lockdown,R.string.place_holder,R.drawable.wifi_password_fill0,
+ {myDpm.hasLockdownAdminConfiguredNetworks(myComponent)},{b -> myDpm.setConfiguredNetworksLockdownState(myComponent,b)}
+ )
+ }
+ if(VERSION.SDK_INT>=33){
+ WifiSecLevel()
+ }
+
+ if(VERSION.SDK_INT>=33&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
+ WifiSsidPolicy()
+ }
+
+ if(VERSION.SDK_INT>=29&&isDeviceOwner(myDpm)){
+ PrivateDNS()
+ }
+
+ if(VERSION.SDK_INT>=26&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)))){
+ NetLog()
+ }
+
+ if(VERSION.SDK_INT>=31&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
+ WifiKeypair()
+ }
+
+ if(VERSION.SDK_INT>=28&&isDeviceOwner(myDpm)){
+ APN()
+ }
+
+ Spacer(Modifier.padding(vertical = 30.dp))
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun WifiSecLevel(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ Column{
+ var selectedWifiSecLevel by remember{mutableIntStateOf(myDpm.minimumRequiredWifiSecurityLevel)}
+ Text(text = stringResource(R.string.min_wifi_security_level), style = typography.titleLarge)
+ RadioButtonItem(stringResource(R.string.wifi_security_level_open), {selectedWifiSecLevel==DevicePolicyManager.WIFI_SECURITY_OPEN}, {selectedWifiSecLevel= DevicePolicyManager.WIFI_SECURITY_OPEN})
+ RadioButtonItem("WEP, WPA(2)-PSK", {selectedWifiSecLevel==DevicePolicyManager.WIFI_SECURITY_PERSONAL}, {selectedWifiSecLevel= DevicePolicyManager.WIFI_SECURITY_PERSONAL})
+ RadioButtonItem("WPA-EAP", {selectedWifiSecLevel==DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_EAP}, {selectedWifiSecLevel= DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_EAP})
+ RadioButtonItem("WPA3-192bit", {selectedWifiSecLevel==DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_192}, {selectedWifiSecLevel= DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_192})
+ 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
+fun WifiSsidPolicy(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val focusMgr = LocalFocusManager.current
+ Column{
+ 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()
+ }
+ val refreshList = {
+ ssidList = ""
+ var count = ssidSet.size
+ for(ssid in ssidSet){ count-=1; ssidList+=ssid; if(count>0){ssidList+="\n"} }
+ }
+ var inited by remember{mutableStateOf(false)}
+ if(!inited){ refreshPolicy(); refreshList(); inited=true }
+ Text(text = stringResource(R.string.wifi_ssid_policy), style = typography.titleLarge)
+ 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!=""){
+ Text(stringResource(R.string.ssid_list_is))
+ SelectionContainer{
+ Text(text = ssidList, color = colorScheme.onPrimaryContainer)
+ }
+ }
+ }
+ OutlinedTextField(
+ value = inputSsid,
+ label = { Text("SSID")},
+ onValueChange = {inputSsid = it},
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
+ keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
+ modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.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()))
+ refreshList()
+ }
+ 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 = ""
+ refreshList()
+ }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))
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun PrivateDNS(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ Text(text = stringResource(R.string.private_dns), style = typography.titleLarge)
+ val dnsStatus = mapOf(
+ DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN to stringResource(R.string.unknown),
+ DevicePolicyManager.PRIVATE_DNS_MODE_OFF to stringResource(R.string.disabled),
+ DevicePolicyManager.PRIVATE_DNS_MODE_OPPORTUNISTIC to stringResource(R.string.auto),
+ DevicePolicyManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME to stringResource(R.string.dns_provide_hostname)
+ )
+ val operationResult = mapOf(
+ DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR to stringResource(R.string.success),
+ DevicePolicyManager.PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING to stringResource(R.string.host_not_serving_dns_tls),
+ DevicePolicyManager.PRIVATE_DNS_SET_ERROR_FAILURE_SETTING to stringResource(R.string.fail)
+ )
+ var status by remember{mutableStateOf(dnsStatus[myDpm.getGlobalPrivateDnsMode(myComponent)])}
+ Text(text = stringResource(R.string.current_state, status?:stringResource(R.string.unknown)))
+ 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 = 3.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().padding(vertical = 2.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
+fun NetLog(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ Column{
+ Text(text = stringResource(R.string.retrieve_net_logs), style = typography.titleLarge)
+ Text(text = stringResource(R.string.developing))
+ Row(modifier=Modifier.fillMaxWidth().padding(horizontal=8.dp),horizontalArrangement=Arrangement.SpaceBetween,verticalAlignment=Alignment.CenterVertically){
+ var checked by remember{mutableStateOf(myDpm.isNetworkLoggingEnabled(myComponent))}
+ Text(text = stringResource(R.string.enabled), style = typography.titleLarge)
+ Switch(
+ checked = checked,
+ onCheckedChange = {myDpm.setNetworkLoggingEnabled(myComponent,!checked);checked = myDpm.isNetworkLoggingEnabled(myComponent)}
+ )
+ }
+ 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
+fun WifiKeypair(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val focusMgr = LocalFocusManager.current
+ Column{
+ var keyPair by remember{mutableStateOf("")}
+ Text(text = stringResource(R.string.wifi_keypair), style = typography.titleLarge)
+ 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().padding(vertical = 2.dp)
+ )
+ val isExist = try{myDpm.isKeyPairGrantedToWifiAuth(keyPair)}catch(e:java.lang.IllegalArgumentException){false}
+ Text(stringResource(R.string.already_exist)+":$isExist")
+ 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
+fun APN(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ val setting = myDpm.getOverrideApns(myComponent)
+ var inputNum by remember{mutableStateOf("0")}
+ var nextStep by remember{mutableStateOf(false)}
+ val builder = Builder()
+ Text(text = stringResource(R.string.apn_settings), style = typography.titleLarge)
+ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically){
+ Text(text = stringResource(R.string.enable), style = typography.titleLarge)
+ Switch(checked = myDpm.isOverrideApnEnabled(myComponent), onCheckedChange = {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))
+ }
+ }
+ }
+ }
+ }
+ }
+ Text(text = stringResource(id = R.string.developing))
+ }
+}
diff --git a/app/src/main/java/com/binbin/androidowner/dpm/Password.kt b/app/src/main/java/com/binbin/androidowner/dpm/Password.kt
new file mode 100644
index 0000000..b10e684
--- /dev/null
+++ b/app/src/main/java/com/binbin/androidowner/dpm/Password.kt
@@ -0,0 +1,484 @@
+package com.binbin.androidowner.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.clickable
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Check
+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.clip
+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 com.binbin.androidowner.R
+import com.binbin.androidowner.ui.CheckBoxItem
+import com.binbin.androidowner.ui.RadioButtonItem
+
+@Composable
+fun Password(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
+ val isWear = sharedPref.getBoolean("isWear",false)
+ val bodyTextStyle = if(isWear){typography.bodyMedium}else{typography.bodyLarge}
+ val scrollState = rememberScrollState()
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier.fillMaxSize().verticalScroll(scrollState)
+ ) {
+ Text(
+ text = stringResource(R.string.password_warning),
+ color = colorScheme.onErrorContainer,
+ style=bodyTextStyle
+ )
+ if(myDpm.isDeviceOwnerApp("com.binbin.androidowner")){
+ Column{
+ 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)),style=bodyTextStyle)
+ }
+ if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)){
+ Text(stringResource(R.string.is_password_sufficient, myDpm.isActivePasswordSufficient),style=bodyTextStyle)
+ }
+ val pwdFailedAttempts = myDpm.currentFailedPasswordAttempts
+ Text(text = stringResource(R.string.password_failed_attempts_is, pwdFailedAttempts),style=bodyTextStyle)
+ if(VERSION.SDK_INT>=28&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
+ val unifiedPwd = myDpm.isUsingUnifiedPassword(myComponent)
+ Text(stringResource(R.string.is_using_unified_password, unifiedPwd),style=bodyTextStyle)
+ }
+ }
+ }
+ if(VERSION.SDK_INT>=26){
+ ResetPasswordToken()
+ }
+
+ ResetPassword()
+
+ PasswordItem(R.string.max_pwd_fail,R.string.max_pwd_fail_desc,R.string.max_pwd_fail_textfield, false,
+ {myDpm.getMaximumFailedPasswordsForWipe(null).toString()},{ic -> myDpm.setMaximumFailedPasswordsForWipe(myComponent, ic.toInt()) })
+ PasswordItem(R.string.pwd_timeout,R.string.pwd_timeout_desc,R.string.pwd_timeout_textfield,true,
+ {myDpm.getPasswordExpiration(null).toString()},{ic -> myDpm.setPasswordExpirationTimeout(myComponent, ic.toLong()) })
+ PasswordItem(R.string.pwd_history,R.string.pwd_history_desc,R.string.pwd_history_textfield,true,
+ {myDpm.getPasswordHistoryLength(null).toString()},{ic -> myDpm.setPasswordHistoryLength(myComponent, ic.toInt()) })
+ PasswordItem(R.string.max_time_to_lock,R.string.max_time_to_lock_desc,R.string.time_unit_ms,true,
+ {myDpm.getMaximumTimeToLock(myComponent).toString()},{ic -> myDpm.setMaximumTimeToLock(myComponent,ic.toLong())})
+
+ if(VERSION.SDK_INT>=31){
+ PasswordComplexity()
+ }
+
+ KeyguardDisabledFeatures()
+
+ PasswordQuality()
+
+ Spacer(Modifier.padding(vertical = 30.dp))
+ }
+}
+
+@Composable
+private fun PasswordItem(
+ itemName:Int,
+ itemDesc:Int,
+ textFieldLabel:Int,
+ allowZero:Boolean,
+ getMethod:()->String,
+ setMethod:(ic:String)->Unit
+){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
+ val isWear = sharedPref.getBoolean("isWear",false)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ var inputContent by remember{ mutableStateOf(if(isDeviceOwner(myDpm)){getMethod()}else{""}) }
+ var ableToApply by remember{ mutableStateOf(inputContent!=""&&((inputContent=="0"&&allowZero)||inputContent!="0")) }
+ Text(text = stringResource(itemName), style = typography.titleLarge,color = colorScheme.onPrimaryContainer)
+ Text(text= stringResource(itemDesc),modifier=Modifier.padding(vertical = 2.dp), style = if(!isWear){typography.bodyLarge}else{typography.bodyMedium})
+ if(!isWear){Spacer(Modifier.padding(vertical = 2.dp))}
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween,
+ modifier = Modifier.fillMaxWidth()
+ ){
+ OutlinedTextField(
+ value = inputContent,
+ label = { Text(stringResource(textFieldLabel))},
+ onValueChange = {
+ inputContent = it
+ ableToApply = inputContent!=""&&((inputContent=="0"&&allowZero)||inputContent!="0")
+ },
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
+ keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
+ enabled = isDeviceOwner(myDpm),
+ modifier = Modifier.focusable().fillMaxWidth(),
+ trailingIcon = {
+ Icon(
+ imageVector = Icons.Outlined.Check, contentDescription = "OK",
+ modifier = Modifier
+ .clip(RoundedCornerShape(50))
+ .clickable(onClick = {focusMgr.clearFocus() ; setMethod(inputContent)}, enabled = isDeviceOwner(myDpm)&&ableToApply)
+ .padding(2.dp)
+ )
+ }
+ )
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun ResetPasswordToken(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::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(horizontalAlignment = Alignment.Start) {
+ Text(text = stringResource(R.string.reset_password_token), style = typography.titleLarge)
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ){
+ 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(0.32F),
+ 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(0.47F)
+ ) {
+ 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(0.88F)
+ ) {
+ Text(stringResource(R.string.activate))
+ }
+ }
+ Text(stringResource(R.string.activate_token_not_required_when_no_password))
+ }
+}
+
+@Composable
+fun ResetPassword(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::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)) }
+ Column{
+ var confirmed by remember{ mutableStateOf(false) }
+ Text(text = stringResource(R.string.reset_password),style = typography.titleLarge)
+ 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().padding(vertical = 5.dp).fillMaxWidth()
+ )
+ Text(text = stringResource(R.string.reset_pwd_desc), modifier = Modifier.padding(vertical = 3.dp))
+ var resetPwdFlag by remember{ mutableIntStateOf(0) }
+ 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})
+ 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}))
+ }
+ 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))
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun PasswordComplexity(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ Column{
+ 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
+ }
+ Text(text = stringResource(R.string.required_password_complexity), style = typography.titleLarge)
+ 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})
+ Text(text = stringResource(R.string.password_ordered_desc), modifier = Modifier.padding(vertical = 3.dp))
+ Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
+ 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(0.4F)
+ ) {
+ Text(text = stringResource(R.string.apply))
+ }
+ Button(
+ onClick = {myContext.startActivity(Intent(ACTION_SET_NEW_PASSWORD))},
+ modifier = Modifier.fillMaxWidth(0.95F)
+ ){
+ Text(stringResource(R.string.require_set_new_password))
+ }
+ }
+ }
+}
+
+@Composable
+fun KeyguardDisabledFeatures(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ Column{
+ 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()
+ }
+ Text(text = stringResource(R.string.keyguard_disabled_features), style = typography.titleLarge)
+ 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}) }
+ }
+ }
+ 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))
+ }
+ }
+}
+
+@Composable
+fun PasswordQuality(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ Column{
+ 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) }
+ Text(text = stringResource(R.string.required_password_quality), style = typography.titleLarge)
+ 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) }
+ 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})
+ Text(text = stringResource(R.string.password_ordered_desc), modifier = Modifier.padding(vertical = 3.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))
+ }
+ if(VERSION.SDK_INT<31){
+ Button(onClick = {myContext.startActivity(Intent(ACTION_SET_NEW_PASSWORD))}){Text(stringResource(R.string.require_set_new_password))}
+ }
+ }
+}
+
+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()
+ }
+}
diff --git a/app/src/main/java/com/binbin/androidowner/dpm/Permissions.kt b/app/src/main/java/com/binbin/androidowner/dpm/Permissions.kt
new file mode 100644
index 0000000..45c7603
--- /dev/null
+++ b/app/src/main/java/com/binbin/androidowner/dpm/Permissions.kt
@@ -0,0 +1,457 @@
+package com.binbin.androidowner.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.material.icons.Icons
+import androidx.compose.material.icons.filled.KeyboardArrowRight
+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.focus.FocusDirection
+import androidx.compose.ui.focus.FocusManager
+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.compose.ui.unit.sp
+import androidx.core.content.ContextCompat.startActivity
+import androidx.navigation.NavHostController
+import com.binbin.androidowner.R
+
+
+@Composable
+fun DpmPermissions(navCtrl:NavHostController){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusManager = LocalFocusManager.current
+ val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
+ val isWear = sharedPref.getBoolean("isWear",false)
+ val titleColor = colorScheme.onPrimaryContainer
+ val bodyTextStyle = if(isWear){typography.bodyMedium}else{typography.bodyLarge}
+ val expandCommandBlock by remember{mutableStateOf("")}
+ Column(
+ modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Row(
+ modifier = Modifier.clickable {navCtrl.navigate("ShizukuActivate")},
+ horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically
+ ){
+ Text(text = "Shizuku", style = typography.titleLarge, color = titleColor, modifier = Modifier.padding(vertical = 2.dp))
+ Icon(imageVector = Icons.Default.KeyboardArrowRight,contentDescription = null, tint = colorScheme.onPrimaryContainer)
+ }
+ DeviceAdmin()
+ ProfileOwner()
+ DeviceOwner()
+ if(VERSION.SDK_INT>=30){
+ Column {
+ Text(text = stringResource(R.string.device_info), style = typography.titleLarge,color = titleColor)
+ if(VERSION.SDK_INT>=34&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
+ val financed = myDpm.isDeviceFinanced
+ Text(stringResource(R.string.is_device_financed, financed),style=bodyTextStyle)
+ }
+ if(VERSION.SDK_INT>=33){
+ val dpmRole = myDpm.devicePolicyManagementRoleHolderPackage
+ Text(stringResource(R.string.dpmrh, if(dpmRole==null) { stringResource(R.string.none) } else { "" }),style=bodyTextStyle)
+ if(dpmRole!=null){
+ SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState())){
+ Text(text = dpmRole, style = bodyTextStyle, color = colorScheme.onPrimaryContainer)
+ }
+ }
+ }
+ val encryptionStatus = mapOf(
+ 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_ACTIVE_DEFAULT_KEY to stringResource(R.string.es_active_default_key),
+ DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER to stringResource(R.string.es_active_per_user),
+ DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING to stringResource(R.string.unknown)
+ )
+ Text("加密状态:${encryptionStatus[myDpm.storageEncryptionStatus]}",style=bodyTextStyle)
+ val adminList = myDpm.activeAdmins
+ if(adminList!=null){
+ var adminListText = ""
+ Text(text = stringResource(R.string.activated_device_admin, adminList.size), style = bodyTextStyle)
+ 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, style = bodyTextStyle, color = titleColor)
+ }
+ }
+ }
+ }
+
+ if(VERSION.SDK_INT>=31&&(isProfileOwner(myDpm)|| isDeviceOwner(myDpm))){
+ SpecificID()
+ }
+
+ if((VERSION.SDK_INT>=26&&isDeviceOwner(myDpm))||(VERSION.SDK_INT>=24&&isProfileOwner(myDpm))){
+ OrgName()
+ }
+
+ if(isDeviceOwner(myDpm) || isProfileOwner(myDpm)){
+ NoManageAccount()
+ }
+
+ if(VERSION.SDK_INT>=24&&isDeviceOwner(myDpm)){
+ DeviceOwnerInfo(R.string.owner_lockscr_info,R.string.place_holder,R.string.owner_lockscr_info,focusManager,myContext,
+ {myDpm.deviceOwnerLockScreenInfo},{content -> myDpm.setDeviceOwnerLockScreenInfo(myComponent,content)})
+ }
+ if(VERSION.SDK_INT>=24&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
+ SupportMsg()
+ }
+
+ if(VERSION.SDK_INT>=28&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
+ TransformOwnership()
+ }
+
+ Spacer(Modifier.padding(vertical = 30.dp))
+ }
+}
+
+@Composable
+fun DeviceOwnerInfo(
+ name:Int,
+ desc:Int,
+ textfield:Int,
+ fm:FocusManager,
+ myContext:Context,
+ input:()->CharSequence?,
+ output:(content:String?)->Unit
+){
+ Column{
+ val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
+ val isWear = sharedPref.getBoolean("isWear",false)
+ Text(text = stringResource(name), style = typography.titleLarge, softWrap = false, color = colorScheme.onPrimaryContainer)
+ if(desc!=R.string.place_holder){
+ Text(
+ text = stringResource(desc),modifier = Modifier.padding(top = 6.dp),
+ style = if(!isWear){typography.bodyLarge}else{typography.bodyMedium})
+ }
+ var inputContent by remember{ mutableStateOf(input()) }
+ OutlinedTextField(
+ value = if(inputContent!=null){ inputContent.toString() }else{""},
+ label = {Text(stringResource(textfield))},
+ onValueChange = { inputContent=it },
+ modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 4.dp)
+ )
+ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
+ Button(
+ onClick = {
+ output(inputContent.toString())
+ inputContent= input()
+ fm.clearFocus()
+ Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
+ },
+ modifier = if(isWear){Modifier.fillMaxWidth(0.49F)}else{Modifier.fillMaxWidth(0.6F)}
+ ) {
+ Text(text = stringResource(R.string.apply))
+ }
+ Button(
+ onClick = {
+ output(null)
+ inputContent = input()
+ fm.clearFocus()
+ Toast.makeText(myContext, myContext.getString(R.string.success), Toast.LENGTH_SHORT).show()
+ },
+ modifier = Modifier.fillMaxWidth(0.96F)
+ ) {
+ Text(text = stringResource(R.string.reset))
+ }
+ }
+ }
+}
+
+@Composable
+fun DeviceAdmin(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ Column {
+ Row(
+ horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically
+ ) {
+ Column {
+ Text(text = "Device Admin")
+ Text(
+ text = stringResource(
+ if(myDpm.isAdminActive(myComponent)) {
+ R.string.activated
+ } else {
+ R.string.deactivated
+ }
+ )
+ )
+ }
+ if(myDpm.isAdminActive(myComponent)) {
+ if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)) {
+ Button(onClick = {
+ myDpm.removeActiveAdmin(myComponent)
+ }) {
+ Text(stringResource(R.string.deactivate))
+ }
+ }
+ } else {
+ Button(onClick = {activateDeviceAdmin(myContext, myComponent)}) {
+ Text(stringResource(R.string.activate))
+ }
+ }
+ }
+ if(!myDpm.isAdminActive(myComponent)) {
+ SelectionContainer {
+ Text(text = stringResource(R.string.activate_device_admin_command), color = colorScheme.onTertiaryContainer)
+ }
+ }
+ }
+}
+
+@Composable
+fun ProfileOwner(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ Column {
+ if(!isDeviceOwner(myDpm)){
+ Row(
+
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Column {
+ Text(text = "Profile Owner")
+ Text(stringResource(if(isProfileOwner(myDpm)){R.string.activated}else{R.string.deactivated}))
+ }
+ if(isProfileOwner(myDpm)&&VERSION.SDK_INT>=24&&!myDpm.isManagedProfile(myComponent)){
+ Button(onClick = {myDpm.clearProfileOwner(myComponent)}) {
+ Text(stringResource(R.string.deactivate))
+ }
+ }
+ }
+ }
+ if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
+ SelectionContainer{
+ Text(text = stringResource(R.string.activate_profile_owner_command), color = colorScheme.onTertiaryContainer)
+ }
+ }
+ }
+}
+
+@Composable
+fun DeviceOwner(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ Column {
+ if(!isProfileOwner(myDpm)){
+ Row(
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Column {
+ Text(text = "Device Owner")
+ Text(stringResource(if(isDeviceOwner(myDpm)){R.string.activated}else{R.string.deactivated}))
+ }
+ if(isDeviceOwner(myDpm)){
+ Button(
+ onClick = {
+ myDpm.clearDeviceOwnerApp(myContext.packageName)
+ }
+ ) {
+ Text(stringResource(R.string.deactivate))
+ }
+ }
+ }
+ }
+
+ if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
+ SelectionContainer{
+ Text(text = stringResource(R.string.activate_device_owner_command), color = colorScheme.onTertiaryContainer)
+ }
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun SpecificID(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ Column{
+ val specificId = myDpm.enrollmentSpecificId
+ Text(text = stringResource(R.string.enrollment_specific_id), style = typography.titleLarge)
+ if(specificId!=""){
+ SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState())){ Text(specificId,softWrap = false) }
+ }else{
+ Text(stringResource(R.string.require_set_org_id))
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun OrgName(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ var orgName by remember{mutableStateOf(try{myDpm.getOrganizationName(myComponent).toString()}catch(e:SecurityException){""})}
+ Text(text = stringResource(R.string.org_name), style = typography.titleLarge)
+ 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()})
+ )
+ 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
+fun SupportMsg(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ DeviceOwnerInfo(R.string.support_msg,R.string.support_msg_desc,R.string.message,focusMgr,myContext,
+ {myDpm.getShortSupportMessage(myComponent)},{content -> myDpm.setShortSupportMessage(myComponent,content)})
+ DeviceOwnerInfo(R.string.long_support_msg,R.string.long_support_msg_desc,R.string.message,focusMgr,myContext,
+ {myDpm.getLongSupportMessage(myComponent)},{content -> myDpm.setLongSupportMessage(myComponent,content)})
+}
+
+@Composable
+fun NoManageAccount(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ Text(text = stringResource(R.string.account_types_management_disabled), style = typography.titleLarge)
+ Text(stringResource(R.string.developing))
+ var noManageAccount = myDpm.accountTypesWithManagementDisabled
+ var accountlist by remember{ mutableStateOf("") }
+ val refreshList = {
+ 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 }
+ 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(bottom = 4.dp),
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
+ keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
+ )
+ Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
+ Button(
+ onClick={
+ focusMgr.clearFocus()
+ myDpm.setAccountManagementDisabled(myComponent,inputText,true)
+ noManageAccount=myDpm.accountTypesWithManagementDisabled
+ refreshList()
+ },
+ modifier = Modifier.fillMaxWidth(0.49f)
+ ){
+ Text(stringResource(R.string.add))
+ }
+ Button(
+ onClick={focusMgr.clearFocus()
+ myDpm.setAccountManagementDisabled(myComponent,inputText,false)
+ noManageAccount=myDpm.accountTypesWithManagementDisabled
+ refreshList()
+ },
+ modifier = Modifier.fillMaxWidth(0.96F)
+ ){
+ Text(stringResource(R.string.remove))
+ }
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun TransformOwnership(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ var pkg by remember{mutableStateOf("")}
+ var cls by remember{mutableStateOf("")}
+ Text(text = stringResource(R.string.transform_ownership), style = typography.titleLarge)
+ Text(text = stringResource(R.string.transform_ownership_desc))
+ OutlinedTextField(
+ value = pkg, onValueChange = {pkg = it}, label = {Text(stringResource(R.string.target_package_name))},
+ modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.dp),
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
+ keyboardActions = KeyboardActions(onNext = {focusMgr.moveFocus(FocusDirection.Down)})
+ )
+ OutlinedTextField(
+ value = cls, onValueChange = {cls = it}, label = {Text(stringResource(R.string.target_class_name))},
+ modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.dp),
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
+ keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
+ )
+ 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().padding(top = 2.dp)
+ ) {
+ Text(stringResource(R.string.transform))
+ }
+ }
+}
+
+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_android_owner_here))
+ startActivity(inputContext,intent,null)
+ }catch(e:ActivityNotFoundException){
+ Toast.makeText(inputContext,inputContext.getString(R.string.unsupported),Toast.LENGTH_SHORT).show()
+ }
+}
diff --git a/app/src/main/java/com/binbin/androidowner/ShizukuActivate.kt b/app/src/main/java/com/binbin/androidowner/dpm/ShizukuActivate.kt
similarity index 97%
rename from app/src/main/java/com/binbin/androidowner/ShizukuActivate.kt
rename to app/src/main/java/com/binbin/androidowner/dpm/ShizukuActivate.kt
index bb14026..23e0d01 100644
--- a/app/src/main/java/com/binbin/androidowner/ShizukuActivate.kt
+++ b/app/src/main/java/com/binbin/androidowner/dpm/ShizukuActivate.kt
@@ -1,4 +1,4 @@
-package com.binbin.androidowner
+package com.binbin.androidowner.dpm
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
@@ -35,6 +35,7 @@ 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.binbin.androidowner.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -58,7 +59,7 @@ fun ShizukuActivate(){
Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState), horizontalAlignment = Alignment.CenterHorizontally){
var outputText by remember{mutableStateOf("")}
if(Binder.getCallingUid()/100000!=0){
- Row(modifier = sections(colorScheme.errorContainer), verticalAlignment = Alignment.CenterVertically){
+ Row{
Icon(imageVector = Icons.Rounded.Warning, contentDescription = null, tint = colorScheme.onErrorContainer)
Text(text = stringResource(R.string.not_primary_user_not_support_shizuku), style = bodyTextStyle, color = colorScheme.onErrorContainer)
}
@@ -86,7 +87,7 @@ fun ShizukuActivate(){
}
if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
- Column(modifier = sections()){
+ Column {
Text(text = stringResource(R.string.activate), style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
if(!myDpm.isAdminActive(myComponent)){
@@ -131,7 +132,7 @@ fun ShizukuActivate(){
}
if(VERSION.SDK_INT>=30&&!isDeviceOwner(myDpm)&&!myDpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)&&!myDpm.isOrganizationOwnedDeviceWithManagedProfile){
- Column(modifier = sections()){
+ 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), style = bodyTextStyle)
var inputUserID by remember{mutableStateOf("")}
diff --git a/app/src/main/java/com/binbin/androidowner/dpm/SystemManage.kt b/app/src/main/java/com/binbin/androidowner/dpm/SystemManage.kt
new file mode 100644
index 0000000..9843112
--- /dev/null
+++ b/app/src/main/java/com/binbin/androidowner/dpm/SystemManage.kt
@@ -0,0 +1,848 @@
+package com.binbin.androidowner.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.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.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.Alignment
+import androidx.compose.ui.Modifier
+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.font.FontWeight
+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 com.binbin.androidowner.R
+import com.binbin.androidowner.sections
+import com.binbin.androidowner.ui.CheckBoxItem
+import com.binbin.androidowner.ui.RadioButtonItem
+import kotlinx.coroutines.delay
+import java.util.Date
+
+@Composable
+fun SystemManage(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
+ if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
+ DeviceCtrlItem(R.string.disable_cam,R.string.place_holder, R.drawable.photo_camera_fill0,
+ {myDpm.getCameraDisabled(null)},{b -> myDpm.setCameraDisabled(myComponent,b)}
+ )
+ }
+ if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
+ DeviceCtrlItem(R.string.disable_scrcap,R.string.aosp_scrrec_also_work,R.drawable.screenshot_fill0,
+ {myDpm.getScreenCaptureDisabled(null)},{b -> myDpm.setScreenCaptureDisabled(myComponent,b) }
+ )
+ }
+ if(VERSION.SDK_INT>=34&&(isDeviceOwner(myDpm)|| (isProfileOwner(myDpm)&&myDpm.isAffiliatedUser))){
+ DeviceCtrlItem(R.string.disable_status_bar,R.string.place_holder,R.drawable.notifications_fill0,
+ {myDpm.isStatusBarDisabled},{b -> myDpm.setStatusBarDisabled(myComponent,b) }
+ )
+ }
+ if(isDeviceOwner(myDpm)||(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile)){
+ if(VERSION.SDK_INT>=30){
+ DeviceCtrlItem(R.string.auto_time,R.string.place_holder,R.drawable.schedule_fill0,
+ {myDpm.getAutoTimeEnabled(myComponent)},{b -> myDpm.setAutoTimeEnabled(myComponent,b) }
+ )
+ DeviceCtrlItem(R.string.auto_timezone,R.string.place_holder,R.drawable.globe_fill0,
+ {myDpm.getAutoTimeZoneEnabled(myComponent)},{b -> myDpm.setAutoTimeZoneEnabled(myComponent,b) }
+ )
+ }else{
+ DeviceCtrlItem(R.string.auto_time,R.string.place_holder,R.drawable.schedule_fill0,{myDpm.autoTimeRequired},{b -> myDpm.setAutoTimeRequired(myComponent,b)})
+ }
+ }
+ if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)){
+ DeviceCtrlItem(R.string.master_mute,R.string.place_holder,R.drawable.volume_up_fill0,
+ {myDpm.isMasterVolumeMuted(myComponent)},{b -> myDpm.setMasterVolumeMuted(myComponent,b) }
+ )
+ }
+ if(VERSION.SDK_INT>=26&&(isDeviceOwner(myDpm)|| isProfileOwner(myDpm))){
+ DeviceCtrlItem(R.string.backup_service,R.string.place_holder,R.drawable.backup_fill0,
+ {myDpm.isBackupServiceEnabled(myComponent)},{b -> myDpm.setBackupServiceEnabled(myComponent,b) }
+ )
+ }
+ if(VERSION.SDK_INT>=23&&(isDeviceOwner(myDpm)|| isProfileOwner(myDpm))){
+ DeviceCtrlItem(R.string.disable_bt_contact_share,R.string.place_holder,R.drawable.account_circle_fill0,
+ {myDpm.getBluetoothContactSharingDisabled(myComponent)},{b -> myDpm.setBluetoothContactSharingDisabled(myComponent,b)}
+ )
+ }
+ if(VERSION.SDK_INT>=30&&isDeviceOwner(myDpm)){
+ DeviceCtrlItem(R.string.common_criteria_mode,R.string.common_criteria_mode_desc,R.drawable.security_fill0,
+ {myDpm.isCommonCriteriaModeEnabled(myComponent)},{b -> myDpm.setCommonCriteriaModeEnabled(myComponent,b)}
+ )
+ }
+ if(VERSION.SDK_INT>=31&&(isDeviceOwner(myDpm)||(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
+ if(myDpm.canUsbDataSignalingBeDisabled()){
+ DeviceCtrlItem(R.string.usb_signal,R.string.place_holder,R.drawable.usb_fill0,
+ {myDpm.isUsbDataSignalingEnabled},{b -> myDpm.isUsbDataSignalingEnabled = b }
+ )
+ }else{
+ Text(text = stringResource(R.string.turn_off_usb_not_support),modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
+ }
+ }
+
+ Keyguard()
+
+ if(VERSION.SDK_INT>=24){
+ Column{
+ 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))
+ }
+ Button(
+ onClick = {myDpm.reboot(myComponent)},
+ enabled = isDeviceOwner(myDpm),
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(stringResource(R.string.reboot))
+ }
+ }
+ }
+
+ if(VERSION.SDK_INT>=28){
+ EditTime()
+ }
+
+ if(VERSION.SDK_INT>=23&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
+ PermissionPolicy()
+ }
+
+ if(VERSION.SDK_INT>=34&&isDeviceOwner(myDpm)){
+ MTEPolicy()
+ }
+
+ if(VERSION.SDK_INT>=31&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
+ NearbyStreamingPolicy()
+ }
+
+ if(VERSION.SDK_INT>=28&&isDeviceOwner(myDpm)){
+ LockTaskFeatures()
+ }
+
+ if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
+ CaCert()
+ }
+
+ if(VERSION.SDK_INT>=26&&(isDeviceOwner(myDpm)||(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
+ SecurityLogs()
+ }
+
+ if(isDeviceOwner(myDpm)){
+ SysUpdatePolicy()
+ }
+
+ WipeData()
+
+ Spacer(Modifier.padding(vertical = 30.dp))
+ }
+}
+
+@Composable
+fun DeviceCtrlItem(
+ itemName:Int,
+ itemDesc:Int,
+ leadIcon:Int,
+ getMethod:()->Boolean,
+ setMethod:(b:Boolean)->Unit
+){
+ var isEnabled by remember{ mutableStateOf(false) }
+ val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
+ val isWear = sharedPref.getBoolean("isWear",false)
+ Row(
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = if(isWear){Modifier.fillMaxWidth(0.65F)}else{Modifier.fillMaxWidth(0.8F)}
+ ){
+ if(!isWear){
+ Icon(
+ painter = painterResource(leadIcon),
+ contentDescription = null,
+ tint = colorScheme.onPrimaryContainer,
+ modifier = Modifier.padding(start = 5.dp, end = 9.dp)
+ )}
+ Column {
+ Text(
+ text = stringResource(itemName),
+ style = if(!isWear){typography.titleLarge}else{typography.titleMedium},
+ color = colorScheme.onPrimaryContainer,
+ fontWeight = if(isWear){ FontWeight.SemiBold }else{ FontWeight.Medium }
+ )
+ if(itemDesc!=R.string.place_holder){ Text(stringResource(itemDesc)) }
+ }
+ }
+ isEnabled = getMethod()
+ Switch(
+ checked = isEnabled,
+ onCheckedChange = {
+ setMethod(!isEnabled)
+ isEnabled=getMethod()
+ },
+ modifier = Modifier.padding(end = 5.dp)
+ )
+ }
+}
+
+@Composable
+fun Keyguard(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ Column{
+ Text(text = stringResource(R.string.keyguard), style = typography.titleLarge,color = colorScheme.onPrimaryContainer)
+ if(VERSION.SDK_INT>=23){
+ Text(text = stringResource(R.string.require_no_password_to_disable))
+ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
+ 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(0.49F)
+ ) {
+ 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(0.96F)
+ ) {
+ Text(stringResource(R.string.enable))
+ }
+ }
+ }
+ 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} }) }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun EditTime(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ Text(text = stringResource(R.string.edit_time), style = typography.titleLarge)
+ var inputTime by remember{mutableStateOf("")}
+ Text(text = stringResource(R.string.from_epoch_to_target_time))
+ 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().padding(vertical = 2.dp)
+ )
+ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
+ Button(
+ onClick = {myDpm.setTime(myComponent,inputTime.toLong())},
+ modifier = Modifier.fillMaxWidth(0.35F),
+ enabled = inputTime!=""&&(isDeviceOwner(myDpm)||(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))
+ ) {
+ Text("应用")
+ }
+ Button(
+ onClick = {inputTime = System.currentTimeMillis().toString()},
+ modifier = Modifier.fillMaxWidth(0.98F)
+ ) {
+ Text(stringResource(R.string.get_current_time))
+ }
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun PermissionPolicy(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ Column{
+ var selectedPolicy by remember{mutableIntStateOf(myDpm.getPermissionPolicy(myComponent))}
+ Text(text = stringResource(R.string.permission_policy), style = typography.titleLarge)
+ 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})
+ 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
+fun MTEPolicy(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ Column{
+ Text(text = stringResource(R.string.mte_policy), style = typography.titleLarge)
+ Text(stringResource(R.string.mte_policy_desc))
+ 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))
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun NearbyStreamingPolicy(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ Column{
+ var appPolicy by remember{mutableIntStateOf(myDpm.nearbyAppStreamingPolicy)}
+ Text(text = stringResource(R.string.nearby_app_streaming), style = typography.titleLarge)
+ 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})
+ Button(
+ onClick = {
+ myDpm.nearbyAppStreamingPolicy = appPolicy
+ Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
+ },
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text("应用")
+ }
+ Spacer(Modifier.padding(vertical = 3.dp))
+ var notificationPolicy by remember{mutableIntStateOf(myDpm.nearbyNotificationStreamingPolicy)}
+ Text(text = stringResource(R.string.nearby_notifi_streaming), style = typography.titleLarge)
+ 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})
+ 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))
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun LockTaskFeatures(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ 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
+ }
+ }
+ Text(text = stringResource(R.string.lock_task_feature), style = typography.titleLarge)
+ 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 = 4.dp))
+ val whitelist = myDpm.getLockTaskPackages(myComponent).toMutableList()
+ var listText by remember{mutableStateOf("")}
+ var inputPkg by remember{mutableStateOf("")}
+ val refreshWhitelist = {
+ inputPkg=""
+ listText=""
+ var currentItem = whitelist.size
+ for(each in whitelist){
+ currentItem-=1
+ listText += each
+ if(currentItem>0){listText += "\n"}
+ }
+ }
+ refreshWhitelist()
+ Text(text = stringResource(R.string.whitelist_app), style = typography.titleLarge)
+ if(listText!=""){
+ SelectionContainer {
+ Text(text = listText)
+ }
+ }else{
+ Text(text = stringResource(R.string.none))
+ }
+ 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))
+ }
+ }
+ }
+}
+
+@Composable
+fun CaCert(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ var exist by remember{mutableStateOf(false)}
+ var isEmpty by remember{mutableStateOf(false)}
+ val refresh = {
+ isEmpty = caCert.isEmpty()
+ exist = if(!isEmpty){ myDpm.hasCaCertInstalled(myComponent, caCert) }else{ false }
+ }
+ LaunchedEffect(exist){ while(true){ delay(600); refresh() } }
+ Column{
+ Text(text = stringResource(R.string.ca_cert), style = typography.titleLarge)
+ if(isEmpty){ Text(text = stringResource(R.string.please_select_ca_cert)) }else{ Text(text = stringResource(R.string.cacert_installed, exist)) }
+ 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
+fun SecurityLogs(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ Column{
+ Text(text = stringResource(R.string.retrieve_security_logs), style = typography.titleLarge)
+ Text(text = stringResource(R.string.developing))
+ Row(modifier=Modifier.fillMaxWidth().padding(horizontal=8.dp),horizontalArrangement=Arrangement.SpaceBetween,verticalAlignment=Alignment.CenterVertically){
+ var checked by remember{mutableStateOf(myDpm.isSecurityLoggingEnabled(myComponent))}
+ Text(text = stringResource(R.string.enabled), style = typography.titleLarge)
+ Switch(
+ checked = checked,
+ onCheckedChange = {myDpm.setSecurityLoggingEnabled(myComponent,!checked);checked=myDpm.isSecurityLoggingEnabled(myComponent)}
+ )
+ }
+ 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
+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,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ 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("")}
+ Text(text = stringResource(R.string.wipe_data),style = typography.titleLarge,modifier = Modifier.padding(6.dp),color = colorScheme.onErrorContainer)
+ CheckBoxItem(stringResource(R.string.wipe_external_storage),{externalStorage},{externalStorage=!externalStorage;confirmed=false}, colorScheme.onErrorContainer)
+ if(VERSION.SDK_INT>=22&&isDeviceOwner(myDpm)){
+ CheckBoxItem(stringResource(R.string.wipe_reset_protection_data),{protectionData},{protectionData=!protectionData;confirmed=false}, colorScheme.onErrorContainer)
+ }
+ if(VERSION.SDK_INT>=28){ CheckBoxItem(stringResource(R.string.wipe_euicc),{euicc},{euicc=!euicc;confirmed=false}, colorScheme.onErrorContainer) }
+ if(VERSION.SDK_INT>=29){ CheckBoxItem(stringResource(R.string.wipe_silently),{silent},{silent=!silent;confirmed=false}, colorScheme.onErrorContainer) }
+ 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)
+ )
+ }
+ 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 }))
+ }
+ Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {
+ 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(if(VERSION.SDK_INT >= 34&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){0.49F}else{1F})
+ ) {
+ 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(0.96F)
+ ) {
+ Text("WipeDevice")
+ }
+ }
+ }
+ if(VERSION.SDK_INT>=24&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
+ Text(text = stringResource(R.string.will_delete_work_profile))
+ }
+ if(VERSION.SDK_INT>=34&&Binder.getCallingUid()/100000==0){
+ Text(text = stringResource(R.string.api34_or_above_wipedata_cannot_in_system_user))
+ }
+ }
+}
+
+@Composable
+fun SysUpdatePolicy(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::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 {
+ if(VERSION.SDK_INT>=26&&isDeviceOwner(myDpm)){
+ val sysUpdateInfo = myDpm.getPendingSystemUpdate(myComponent)
+ Column(modifier = sections()) {
+ if(sysUpdateInfo!=null){
+ Text(text = "Update first available: ${Date(sysUpdateInfo.receivedTime)}", style = bodyTextStyle)
+ Text(text = "Hash code: ${sysUpdateInfo.hashCode()}", 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)
+ }
+ }
+ }
+ if(VERSION.SDK_INT>=23){
+ Column(modifier = sections()) {
+ var selectedPolicy by remember{ mutableStateOf(myDpm.systemUpdatePolicy?.policyType) }
+ Text(text = stringResource(R.string.system_update_policy), style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
+ RadioButtonItem(stringResource(R.string.system_update_policy_automatic),{selectedPolicy==SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC},{selectedPolicy= SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC})
+ RadioButtonItem(stringResource(R.string.system_update_policy_install_windowed),{selectedPolicy==SystemUpdatePolicy.TYPE_INSTALL_WINDOWED},{selectedPolicy= SystemUpdatePolicy.TYPE_INSTALL_WINDOWED})
+ RadioButtonItem(stringResource(R.string.system_update_policy_postpone),{selectedPolicy==SystemUpdatePolicy.TYPE_POSTPONE},{selectedPolicy= SystemUpdatePolicy.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))
+ Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.Center) {
+ 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)
+ }
+ val policy =
+ when(selectedPolicy){
+ SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC-> SystemUpdatePolicy.createAutomaticInstallPolicy()
+ SystemUpdatePolicy.TYPE_INSTALL_WINDOWED-> SystemUpdatePolicy.createWindowedInstallPolicy(windowedPolicyStart.toInt(),windowedPolicyEnd.toInt())
+ SystemUpdatePolicy.TYPE_POSTPONE-> SystemUpdatePolicy.createPostponeInstallPolicy()
+ else->null
+ }
+ Button(
+ onClick = {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>=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("安装")
+ }
+ }
+ }
+ }*/
+ }
+}
diff --git a/app/src/main/java/com/binbin/androidowner/dpm/UserManage.kt b/app/src/main/java/com/binbin/androidowner/dpm/UserManage.kt
new file mode 100644
index 0000000..8c31110
--- /dev/null
+++ b/app/src/main/java/com/binbin/androidowner/dpm/UserManage.kt
@@ -0,0 +1,419 @@
+package com.binbin.androidowner.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.UserHandle
+import android.os.UserManager
+import android.provider.MediaStore
+import android.widget.Toast
+import androidx.activity.ComponentActivity
+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.Button
+import androidx.compose.material3.MaterialTheme.colorScheme
+import androidx.compose.material3.MaterialTheme.typography
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+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.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.unit.dp
+import androidx.core.os.UserManagerCompat
+import com.binbin.androidowner.ui.CheckBoxItem
+import com.binbin.androidowner.ui.RadioButtonItem
+import com.binbin.androidowner.uriToStream
+
+var affiliationID = mutableSetOf()
+@Composable
+fun UserManage() {
+ Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val userManager = myContext.getSystemService(Context.USER_SERVICE) as UserManager
+ val titleColor = colorScheme.onPrimaryContainer
+ Column{
+ Text(text = "用户信息", style = typography.titleLarge, color = titleColor)
+ Text("用户已解锁:${UserManagerCompat.isUserUnlocked(myContext)}")
+ if(VERSION.SDK_INT>=24){ Text("支持多用户:${UserManager.supportsMultipleUsers()}") }
+ if(VERSION.SDK_INT>=23){ Text(text = "系统用户:${userManager.isSystemUser}") }
+ if(VERSION.SDK_INT>=34){ Text(text = "管理员用户:${userManager.isAdminUser}") }
+ if(VERSION.SDK_INT>=31){ Text(text = "无头系统用户: ${UserManager.isHeadlessSystemUserMode()}") }
+ Spacer(Modifier.padding(vertical = 5.dp))
+ if (VERSION.SDK_INT >= 28) {
+ val logoutable = myDpm.isLogoutEnabled
+ Text(text = "用户可以退出 : $logoutable")
+ if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)){
+ val ephemeralUser = myDpm.isEphemeralUser(myComponent)
+ Text(text = "临时用户: $ephemeralUser")
+ }
+ Text(text = "附属用户: ${myDpm.isAffiliatedUser}")
+ }
+ Spacer(Modifier.padding(vertical = 5.dp))
+ Text(text = "当前UserID:${Binder.getCallingUid()/100000}")
+ Text(text = "当前用户序列号:${userManager.getSerialNumberForUser(android.os.Process.myUserHandle())}")
+ }
+
+ UserOperation()
+
+ if(VERSION.SDK_INT>=24&&isDeviceOwner(myDpm)){
+ CreateUser()
+ }
+
+ if(VERSION.SDK_INT>=26&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
+ AffiliationID()
+ }
+
+ UserSessionMessage("用户名", "用户名", true, {null}) {msg-> myDpm.setProfileName(myComponent, msg.toString())}
+
+ if(VERSION.SDK_INT>=23&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
+ UserIcon()
+ }
+
+ if(VERSION.SDK_INT>=28){
+ UserSessionMessage("用户会话开始消息", "消息", false, {myDpm.getStartUserSessionMessage(myComponent)}) {msg-> myDpm.setStartUserSessionMessage(myComponent, msg)}
+ UserSessionMessage("用户会话结束消息", "消息", false, {myDpm.getEndUserSessionMessage(myComponent)}) {msg-> myDpm.setEndUserSessionMessage(myComponent, msg)}
+ }
+ Spacer(Modifier.padding(vertical = 30.dp))
+ }
+}
+
+@Composable
+private fun UserSessionMessage(text:String, textField:String, profileOwner:Boolean, get: ()->CharSequence?, setMsg:(msg: CharSequence?)->Unit){
+ Column{
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val focusMgr = LocalFocusManager.current
+ var msg by remember{ mutableStateOf(if(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&profileOwner)){ if(get()==null){""}else{get().toString()} }else{""}) }
+ val sharedPref = myContext.getSharedPreferences("data", Context.MODE_PRIVATE)
+ val isWear = sharedPref.getBoolean("isWear",false)
+ Text(text = text, style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
+ TextField(
+ value = msg,
+ onValueChange = {msg=it},
+ label = {Text(textField)},
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
+ keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
+ modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 6.dp),
+ enabled = isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&profileOwner)
+ )
+ Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {
+ Button(
+ onClick = {
+ focusMgr.clearFocus()
+ setMsg(msg)
+ msg = if(get()==null){""}else{get().toString()}
+ Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
+ },
+ enabled = isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&profileOwner),
+ modifier = Modifier.fillMaxWidth(if(isWear){0.49F}else{0.65F})
+ ) {
+ Text("应用")
+ }
+ Button(
+ onClick = {
+ focusMgr.clearFocus()
+ setMsg(null)
+ msg = get()?.toString() ?: ""
+ Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
+ },
+ enabled = isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&profileOwner),
+ modifier = Modifier.fillMaxWidth(0.96F)
+ ) {
+ Text("默认")
+ }
+ }
+ }
+}
+
+@Composable
+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,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ Text(text = "用户操作", style = typography.titleLarge)
+ var idInput by remember{ mutableStateOf("") }
+ var userHandleById:UserHandle by remember{ mutableStateOf(android.os.Process.myUserHandle()) }
+ var useUid by remember{ mutableStateOf(false) }
+ TextField(
+ 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 ?: android.os.Process.myUserHandle()
+ }
+ },
+ label = {Text(if(useUid){"UID"}else{"序列号"})},
+ enabled = isDeviceOwner(myDpm),
+ modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 3.dp),
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
+ keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
+ )
+ if(VERSION.SDK_INT>=24&&isDeviceOwner(myDpm)){
+ CheckBoxItem(text = "使用UID", checked = {useUid}, operation = {idInput=""; useUid = !useUid})
+ }
+ if(VERSION.SDK_INT>28){
+ if(isProfileOwner(myDpm)&&myDpm.isAffiliatedUser){
+ Button(
+ onClick = {
+ val result = myDpm.logoutUser(myComponent)
+ Toast.makeText(myContext, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
+ },
+ enabled = isProfileOwner(myDpm),
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text("登出当前用户")
+ }
+ }
+ }
+ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
+ Button(
+ onClick = {
+ focusMgr.clearFocus()
+ if(VERSION.SDK_INT>=28){
+ val result = myDpm.startUserInBackground(myComponent,userHandleById)
+ Toast.makeText(myContext, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
+ }
+ },
+ enabled = isDeviceOwner(myDpm)&&VERSION.SDK_INT>=28,
+ modifier = Modifier.fillMaxWidth(0.49F)
+ ){
+ Text("在后台启动")
+ }
+ Button(
+ onClick = {
+ focusMgr.clearFocus()
+ if(myDpm.switchUser(myComponent,userHandleById)){
+ Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
+ }else{
+ Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
+ }
+ },
+ enabled = isDeviceOwner(myDpm),
+ modifier = Modifier.fillMaxWidth(0.96F)
+ ) {
+ Text("切换")
+ }
+ }
+ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
+ Button(
+ onClick = {
+ focusMgr.clearFocus()
+ try{
+ if(VERSION.SDK_INT>=28){
+ val result = myDpm.stopUser(myComponent,userHandleById)
+ Toast.makeText(myContext, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
+ }
+ }catch(e:IllegalArgumentException){
+ Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
+ }
+ },
+ enabled = isDeviceOwner(myDpm)&&VERSION.SDK_INT>=28,
+ modifier = Modifier.fillMaxWidth(0.49F)
+ ) {
+ Text("停止")
+ }
+ Button(
+ onClick = {
+ focusMgr.clearFocus()
+ if(myDpm.removeUser(myComponent,userHandleById)){
+ Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
+ idInput=""
+ }else{
+ Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
+ }
+ },
+ enabled = isDeviceOwner(myDpm),
+ modifier = Modifier.fillMaxWidth(0.96F)
+ ) {
+ Text("移除")
+ }
+ }
+ if(VERSION.SDK_INT<28){
+ Text(text = "停止用户需API28")
+ }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+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,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ var userName by remember{ mutableStateOf("") }
+ Text(text = "创建用户", style = typography.titleLarge)
+ TextField(
+ value = userName,
+ onValueChange = {userName=it},
+ label = {Text("用户名")},
+ modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 4.dp),
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
+ keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
+ )
+ var selectedFlag by remember{ mutableIntStateOf(0) }
+ RadioButtonItem("无",{selectedFlag==0},{selectedFlag=0})
+ RadioButtonItem("跳过创建用户向导",{selectedFlag==DevicePolicyManager.SKIP_SETUP_WIZARD},{selectedFlag=DevicePolicyManager.SKIP_SETUP_WIZARD})
+ if(VERSION.SDK_INT>=28){
+ RadioButtonItem("临时用户",{selectedFlag==DevicePolicyManager.MAKE_USER_EPHEMERAL},{selectedFlag=DevicePolicyManager.MAKE_USER_EPHEMERAL})
+ RadioButtonItem("启用所有系统应用",{selectedFlag==DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED},{selectedFlag=DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED})
+ }
+ var newUserHandle: UserHandle? by remember{ mutableStateOf(null) }
+ 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("创建(Owner)")
+ }
+ if(newUserHandle!=null){ Text(text = "新用户的序列号:${userManager.getSerialNumberForUser(newUserHandle)}") }
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun AffiliationID(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ val focusMgr = LocalFocusManager.current
+ Column{
+ var input by remember{mutableStateOf("")}
+ var list by remember{mutableStateOf("")}
+ val refresh = {
+ list = ""
+ var count = affiliationID.size
+ for(item in affiliationID){ count-=1; list+=item; if(count>0){list+="\n"} }
+ }
+ var inited by remember{mutableStateOf(false)}
+ if(!inited){affiliationID = myDpm.getAffiliationIds(myComponent);refresh();inited=true}
+ Text(text = "附属用户ID", style = typography.titleLarge)
+ TextField(
+ 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()})
+ )
+ if(list!=""){
+ SelectionContainer {
+ Text(text = list)
+ }
+ }else{
+ Text(text = "无")
+ }
+ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
+ Button(
+ onClick = { affiliationID.add(input); refresh() },
+ modifier = Modifier.fillMaxWidth(0.49F)
+ ){
+ Text("添加")
+ }
+ Button(
+ onClick = { affiliationID.remove(input); refresh() },
+ modifier = Modifier.fillMaxWidth(0.96F)
+ ){
+ Text("移除")
+ }
+ }
+ Button(
+ onClick = {
+ if("" in affiliationID) {
+ Toast.makeText(myContext, "有空字符串", Toast.LENGTH_SHORT).show()
+ }else if(affiliationID.isEmpty()){
+ Toast.makeText(myContext, "不能为空", Toast.LENGTH_SHORT).show()
+ }else{
+ myDpm.setAffiliationIds(myComponent, affiliationID)
+ affiliationID = myDpm.getAffiliationIds(myComponent)
+ refresh()
+ Toast.makeText(myContext,"成功",Toast.LENGTH_SHORT).show()
+ }
+ },
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text("应用")
+ }
+ Text(text = "如果多用户,附属用户ID相同时可以让其他用户附属于主用户")
+ }
+}
+
+@SuppressLint("NewApi")
+@Composable
+fun UserIcon(){
+ val myContext = LocalContext.current
+ val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
+ val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
+ Column{
+ var getContent by remember{mutableStateOf(false)}
+ Text(text = "用户图标", style = typography.titleLarge)
+ Text(text = "尽量选择正方形的图片,以免产生问题")
+ CheckBoxItem("使用文件选择器而不是相册",{getContent},{getContent=!getContent})
+ 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("选择图片...")
+ }
+ Button(
+ onClick = {
+ if(userIconUri!=null){
+ uriToStream(myContext, userIconUri){stream ->
+ val bitmap = BitmapFactory.decodeStream(stream)
+ myDpm.setUserIcon(myComponent,bitmap)
+ Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
+ }
+ }else{
+ Toast.makeText(myContext, "请先选择图片", Toast.LENGTH_SHORT).show()
+ }
+ },
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text("应用")
+ }
+ }
+}
+
+private fun userOperationResultCode(result:Int): String {
+ return when(result){
+ UserManager.USER_OPERATION_SUCCESS->"成功"
+ UserManager.USER_OPERATION_ERROR_UNKNOWN->"未知结果(失败)"
+ UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE->"失败:受管理的资料"
+ UserManager.USER_OPERATION_ERROR_CURRENT_USER->"失败:当前用户"
+ else->"未知"
+ }
+}
diff --git a/app/src/main/java/com/binbin/androidowner/UserRestrict.kt b/app/src/main/java/com/binbin/androidowner/dpm/UserRestriction.kt
similarity index 91%
rename from app/src/main/java/com/binbin/androidowner/UserRestrict.kt
rename to app/src/main/java/com/binbin/androidowner/dpm/UserRestriction.kt
index 109e367..e6ae491 100644
--- a/app/src/main/java/com/binbin/androidowner/UserRestrict.kt
+++ b/app/src/main/java/com/binbin/androidowner/dpm/UserRestriction.kt
@@ -1,4 +1,4 @@
-package com.binbin.androidowner
+package com.binbin.androidowner.dpm
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
@@ -28,9 +28,9 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
+import com.binbin.androidowner.R
private data class Restriction(
val restriction:String,
@@ -50,22 +50,16 @@ fun UserRestriction(){
var mediaVisible by remember{ mutableStateOf(false) }
var userVisible by remember{ mutableStateOf(false) }
var otherVisible by remember{ mutableStateOf(false) }
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- val isWear = sharedPref.getBoolean("isWear",false)
- val bodyTextStyle = if(isWear){typography.bodyMedium}else{typography.bodyLarge}
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally){
- Text(text = "打开开关后会禁用对应的功能",style = bodyTextStyle)
+ Text(text = "打开开关后会禁用对应的功能")
if(VERSION.SDK_INT<24){
- Text(text = "所有的用户限制都需要API24,你的设备低于API24,无法使用。", style = bodyTextStyle, color = colorScheme.error)
+ Text(text = "所有的用户限制都需要API24,你的设备低于API24,无法使用。", color = colorScheme.error)
}
if(isProfileOwner(myDpm)){
- Text(text = "Profile owner无法使用部分功能", style = bodyTextStyle)
+ Text(text = "Profile owner无法使用部分功能")
}
if(isProfileOwner(myDpm)&&(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isManagedProfile(myComponent)))){
- Text(text = "工作资料中部分功能无效", style = bodyTextStyle)
- }
- if(isWear){
- Text(text = "部分功能在手表上无效", style = typography.bodyMedium)
+ Text(text = "工作资料中部分功能无效")
}
SectionTab("网络和互联网",{internetVisible}, { internetVisible=!internetVisible})
AnimatedVisibility(internetVisible) {
@@ -148,33 +142,28 @@ private fun UserRestrictionItem(
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
var strictState by remember{ mutableStateOf(false) }
- val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
- val isWear = sharedPref.getBoolean("isWear",false)
Row(
- modifier = sections(colorScheme.secondaryContainer),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
){
Row(
verticalAlignment = Alignment.CenterVertically,
- modifier = if(isWear){Modifier.fillMaxWidth(0.65F)}else{Modifier.fillMaxWidth(0.8F)}
+ modifier = Modifier.fillMaxWidth(0.8F)
) {
- if(!isWear){
Icon(
painter = painterResource(leadIcon),
contentDescription = null,
modifier = Modifier.padding(start = 4.dp, end = 8.dp),
tint = colorScheme.secondary
- )}
+ )
Column{
Text(
text = stringResource(itemName),
- style = if(!isWear){typography.titleLarge}else{typography.titleMedium},
- color = colorScheme.onSecondaryContainer,
- fontWeight = if(isWear){ FontWeight.SemiBold }else{ FontWeight.Medium }
+ style = typography.titleLarge,
+ color = colorScheme.onSecondaryContainer
)
if(restrictionDescription!=""){
- Text(text = restrictionDescription, color = colorScheme.onSecondaryContainer, style = if(isWear){typography.bodyMedium}else{typography.bodyLarge})
+ Text(text = restrictionDescription, color = colorScheme.onSecondaryContainer, style = typography.titleLarge)
}
}
}
@@ -200,7 +189,7 @@ private fun UserRestrictionItem(
strictState = myDpm.getUserRestrictions(myComponent).getBoolean(restriction)
},
enabled = isDeviceOwner(myDpm)|| isProfileOwner(myDpm),
- modifier = Modifier.padding(end = if(!isWear){5.dp}else{0.dp})
+ modifier = Modifier.padding(end = 5.dp)
)
}
}
@@ -209,7 +198,7 @@ private fun UserRestrictionItem(
private class RestrictionData{
fun internet():List{
val list:MutableList = mutableListOf()
- list += Restriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,R.string.config_mobile_network,"",R.drawable.signal_cellular_alt_fill0)
+ 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){
diff --git a/app/src/main/java/com/binbin/androidowner/ui/Components.kt b/app/src/main/java/com/binbin/androidowner/ui/Components.kt
new file mode 100644
index 0000000..bef6de3
--- /dev/null
+++ b/app/src/main/java/com/binbin/androidowner/ui/Components.kt
@@ -0,0 +1,67 @@
+package com.binbin.androidowner.ui
+
+import android.content.Context
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.MaterialTheme.colorScheme
+import androidx.compose.material3.MaterialTheme.typography
+import androidx.compose.material3.RadioButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+
+
+@Composable
+fun RadioButtonItem(
+ text:String,
+ selected:()->Boolean,
+ operation:()->Unit,
+ textColor: Color = colorScheme.onBackground
+){
+ val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
+ val isWear = sharedPref.getBoolean("isWear",false)
+ Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = if(isWear){3.dp}else{0.dp})
+ .clip(RoundedCornerShape(25))
+ .clickable(onClick = operation)
+ ) {
+ RadioButton(selected = selected(), onClick = operation,modifier=if(isWear){Modifier.size(28.dp)}else{Modifier})
+ Text(text = text, style = if(!isWear){typography.bodyLarge}else{typography.bodyMedium}, color = textColor,
+ modifier = Modifier.padding(bottom = 2.dp))
+ }
+}
+
+@Composable
+fun CheckBoxItem(
+ text:String,
+ checked:()->Boolean,
+ operation:()->Unit,
+ textColor:Color = colorScheme.onBackground
+){
+ val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
+ val isWear = sharedPref.getBoolean("isWear",false)
+ Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = if(isWear){3.dp}else{0.dp})
+ .clip(RoundedCornerShape(25))
+ .clickable(onClick = operation)
+ ) {
+ Checkbox(
+ checked = checked(),
+ onCheckedChange = {operation()},
+ modifier=if(isWear){Modifier.size(28.dp)}else{Modifier}
+ )
+ Text(text = text, style = if(!isWear){typography.bodyLarge}else{typography.bodyMedium}, color = textColor, modifier = Modifier.padding(bottom = 2.dp))
+ }
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4a7a202..668f403 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -85,11 +85,11 @@
列出Owners
服务未启动
- dpm mark-profile-owner-on-organization-owned-device --user %1$s com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver
+ dpm mark-profile-owner-on-organization-owned-device --user %1$s com.binbin.androidowner/com.binbin.androidowner.dpm.MyDeviceAdminReceiver
- dpm set-device-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver
- dpm set-profile-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver
- dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver
+ dpm set-device-owner com.binbin.androidowner/com.binbin.androidowner.dpm.MyDeviceAdminReceiver
+ dpm set-profile-owner com.binbin.androidowner/com.binbin.androidowner.dpm.MyDeviceAdminReceiver
+ dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.dpm.MyDeviceAdminReceiver
已授权(Shell)
已授权(Root)