diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 47bb4c3..5614c0d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "com.binbin.androidowner" minSdk = 26 targetSdk = 34 - versionCode = 1 - versionName = "1.0" + versionCode = 2 + versionName = "1.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/java/com/binbin/androidowner/ApplicationManage.kt b/app/src/main/java/com/binbin/androidowner/ApplicationManage.kt index cbdf106..962f929 100644 --- a/app/src/main/java/com/binbin/androidowner/ApplicationManage.kt +++ b/app/src/main/java/com/binbin/androidowner/ApplicationManage.kt @@ -2,13 +2,20 @@ package com.binbin.androidowner import android.app.admin.DevicePolicyManager import android.content.ComponentName +import android.content.Context import android.content.pm.PackageManager.NameNotFoundException +import android.os.Build.VERSION +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable @@ -18,10 +25,13 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp + @Composable -fun ApplicationManage(myDpm:DevicePolicyManager, myComponent:ComponentName){ +fun ApplicationManage(myDpm:DevicePolicyManager, myComponent:ComponentName,myContext:Context){ var pkgName by remember { mutableStateOf("") } Column( modifier = Modifier @@ -29,59 +39,83 @@ fun ApplicationManage(myDpm:DevicePolicyManager, myComponent:ComponentName){ .padding(8.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - var isAppHidden by remember{ mutableStateOf(false) } - var isAppSuspended by remember{ mutableStateOf(false) } - var isAppUninstallBlock by remember{ mutableStateOf(false) } - var suspendedReply = "" - isAppHidden = try { - myDpm.isApplicationHidden(myComponent,pkgName) - }catch (e:SecurityException){ - false - } - isAppUninstallBlock = try { - myDpm.isUninstallBlocked(myComponent,pkgName) - }catch (e:SecurityException){ - false - } - try{ - isAppSuspended = myDpm.isPackageSuspended(myComponent,pkgName) - }catch(e:NameNotFoundException){ - suspendedReply = "应用不存在!" - isAppSuspended = false - }catch (e:SecurityException){ - suspendedReply = "无权限" - isAppSuspended = false - } Text("以下功能都需要DeviceOwner权限") - TextField(value = pkgName, onValueChange = {pkgName = it}, label = { Text("包名") }) - Spacer(Modifier.padding(5.dp)) - Row{ - Button(onClick = { myDpm.setApplicationHidden(myComponent,pkgName,true); isAppHidden = myDpm.isApplicationHidden(myComponent,pkgName) },modifier = Modifier.padding(end = 8.dp)) { - Text("隐藏") - } - Button(onClick = { myDpm.setApplicationHidden(myComponent,pkgName,false); isAppHidden = myDpm.isApplicationHidden(myComponent,pkgName) }) { - Text("显示") + TextField( + value = pkgName, + onValueChange = { + pkgName = it + }, + label = { Text("包名") } + ) + val isSuspended = { + try{ + myDpm.isPackageSuspended(myComponent,pkgName) + }catch(e:NameNotFoundException){ + false } } - Text("应用隐藏:$isAppHidden ${if(isAppHidden){"(这个应用也许没有被安装)"}else{""}}") - Row{ - Button(onClick = {myDpm.setPackagesSuspended(myComponent, arrayOf(pkgName),true); isAppSuspended = myDpm.isPackageSuspended(myComponent,pkgName)},modifier = Modifier.padding(end = 8.dp)) { - Text("停用") + AppManageItem(R.string.hide,R.string.isapphidden_desc,myDpm, {myDpm.isApplicationHidden(myComponent,pkgName)}, + {b -> myDpm.setApplicationHidden(myComponent,pkgName,b)}) + AppManageItem(R.string.suspend,R.string.place_holder,myDpm, isSuspended, + {b -> myDpm.setPackagesSuspended(myComponent, arrayOf(pkgName) ,b)}) + /*AppManageItem(R.string.block_unins,R.string.sometimes_not_avaliable,myDpm, {myDpm.isUninstallBlocked(myComponent,pkgName)}, + {b -> myDpm.setUninstallBlocked(myComponent,pkgName,b)})*/ + Text("因为无法获取某个应用是否防卸载,无法使用开关控制防卸载") + Row { + Button(onClick = {myDpm.setUninstallBlocked(myComponent,pkgName,false)}) { + Text("取消防卸载") } - Button(onClick = {myDpm.setPackagesSuspended(myComponent, arrayOf(pkgName),false); isAppSuspended = myDpm.isPackageSuspended(myComponent,pkgName)}) { - Text("启用") + Spacer(Modifier.padding(horizontal = 2.dp)) + Button(onClick = {myDpm.setUninstallBlocked(myComponent,pkgName,true)}) { + Text("防卸载") } } - Text("应用停用:$isAppSuspended $suspendedReply") - Text("阻止卸载功能有可能出问题") - Row { - Button(onClick = { myDpm.setUninstallBlocked(myComponent, pkgName, true); isAppUninstallBlock = myDpm.isUninstallBlocked(myComponent,pkgName) },modifier = Modifier.padding(end = 8.dp)) { - Text("阻止卸载") - } - Button(onClick = { myDpm.setUninstallBlocked(myComponent, pkgName, false); isAppUninstallBlock = myDpm.isUninstallBlocked(myComponent,pkgName)}) { - Text("允许卸载") + if(VERSION.SDK_INT>=30){ + AppManageItem(R.string.user_ctrl_disabled,R.string.user_ctrl_disabled_desc,myDpm, {pkgName in myDpm.getUserControlDisabledPackages(myComponent)}, + {b->myDpm.setUserControlDisabledPackages(myComponent, mutableListOf(if(b){pkgName}else{null}))}) + } + Spacer(Modifier.padding(5.dp)) + } +} + +@Composable +private fun AppManageItem( + itemName:Int, + itemDesc:Int, + myDpm: DevicePolicyManager, + getMethod:()->Boolean, + setMethod:(b:Boolean)->Unit +){ + var isEnabled by remember{ mutableStateOf(false) } + if(myDpm.isDeviceOwnerApp("com.binbin.androidowner")){ + isEnabled = getMethod() + } + Row( + modifier = Modifier + .fillMaxWidth() + .padding(5.dp) + .clip(RoundedCornerShape(15)) + .background(color = MaterialTheme.colorScheme.primaryContainer) + .padding(8.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Column { + Text( + text = stringResource(itemName), + style = MaterialTheme.typography.titleLarge + ) + if(itemDesc!=R.string.place_holder){ + Text(stringResource(itemDesc)) } } - Text("应用防卸载:$isAppUninstallBlock ${if(!isAppUninstallBlock){"(这个应用也许没有被安装)"}else{""}}") + Switch( + checked = isEnabled, + onCheckedChange = { + setMethod(!isEnabled) + isEnabled = getMethod() + }, + enabled = myDpm.isDeviceOwnerApp("com.binbin.androidowner") + ) } } diff --git a/app/src/main/java/com/binbin/androidowner/DeviceControl.kt b/app/src/main/java/com/binbin/androidowner/DeviceControl.kt index b110770..9ce6def 100644 --- a/app/src/main/java/com/binbin/androidowner/DeviceControl.kt +++ b/app/src/main/java/com/binbin/androidowner/DeviceControl.kt @@ -93,7 +93,7 @@ fun DeviceControl(myDpm: DevicePolicyManager, myComponent: ComponentName){ } @Composable -fun DeviceCtrlItem( +private fun DeviceCtrlItem( itemName:Int, itemDesc:Int, myDpm: DevicePolicyManager, @@ -122,13 +122,14 @@ fun DeviceCtrlItem( } if(myDpm.isDeviceOwnerApp("com.binbin.androidowner")){ isEnabled = getMethod() - Switch( - checked = isEnabled, - onCheckedChange = { - setMethod(!isEnabled) - isEnabled=getMethod() - } - ) } + Switch( + checked = isEnabled, + onCheckedChange = { + setMethod(!isEnabled) + isEnabled=getMethod() + }, + enabled = myDpm.isDeviceOwnerApp("com.binbin.androidowner") + ) } } diff --git a/app/src/main/java/com/binbin/androidowner/MainActivity.kt b/app/src/main/java/com/binbin/androidowner/MainActivity.kt index dc5a781..2af3632 100644 --- a/app/src/main/java/com/binbin/androidowner/MainActivity.kt +++ b/app/src/main/java/com/binbin/androidowner/MainActivity.kt @@ -129,8 +129,8 @@ fun MyScaffold(mainDpm:DevicePolicyManager, mainComponent:ComponentName, mainCon ){ composable(route = "HomePage", content = { HomePage(navCtrl,mainDpm,mainComponent)}) composable(route = "DeviceControl", content = { DeviceControl(mainDpm,mainComponent)}) - composable(route = "Permissions", content = { DpmPermissions(mainDpm,mainComponent,mainContext)}) - composable(route = "ApplicationManage", content = { ApplicationManage(mainDpm,mainComponent)}) + composable(route = "Permissions", content = { DpmPermissions(mainDpm,mainComponent,mainContext,navCtrl)}) + composable(route = "ApplicationManage", content = { ApplicationManage(mainDpm,mainComponent,mainContext)}) composable(route = "UserRestriction", content = { UserRestriction(mainDpm,mainComponent)}) } } @@ -140,7 +140,7 @@ fun MyScaffold(mainDpm:DevicePolicyManager, mainComponent:ComponentName, mainCon fun HomePage(navCtrl:NavHostController,myDpm:DevicePolicyManager,myComponent:ComponentName){ val isda = myDpm.isAdminActive(myComponent) val isdo = myDpm.isDeviceOwnerApp("com.binbin.androidowner") - val activated = if(isdo){"Device Owner 已激活"}else if(isda){"Device Admin已激活"}else{""} + "未激活" + val activated = if(isdo){"Device Owner 已激活"}else if(isda){"Device Admin已激活"}else{"未激活"} Column { Row( modifier = Modifier diff --git a/app/src/main/java/com/binbin/androidowner/Permissions.kt b/app/src/main/java/com/binbin/androidowner/Permissions.kt index 87e1ced..d55cae8 100644 --- a/app/src/main/java/com/binbin/androidowner/Permissions.kt +++ b/app/src/main/java/com/binbin/androidowner/Permissions.kt @@ -4,7 +4,8 @@ import android.app.admin.DevicePolicyManager import android.content.ComponentName import android.content.Context import android.content.Intent -import android.widget.Toast +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.provider.Settings.Global import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -29,11 +30,14 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp +import androidx.core.app.ActivityCompat.startActivityForResult import androidx.core.content.ContextCompat.startActivity +import androidx.navigation.NavGraph.Companion.findStartDestination +import androidx.navigation.NavHostController @Composable -fun DpmPermissions(myDpm: DevicePolicyManager, myComponent: ComponentName, myContext:Context){ +fun DpmPermissions(myDpm: DevicePolicyManager, myComponent: ComponentName, myContext:Context,navCtrl:NavHostController){ //da:DeviceAdmin do:DeviceOwner val isda = myDpm.isAdminActive(myComponent) val isdo = myDpm.isDeviceOwnerApp("com.binbin.androidowner") @@ -58,7 +62,16 @@ fun DpmPermissions(myDpm: DevicePolicyManager, myComponent: ComponentName, myCon Text(if(isda){"已激活"}else{"未激活"}) } if(isda){ - Button(onClick = {myDpm.removeActiveAdmin(myComponent)}) { + Button( + onClick = { + myDpm.removeActiveAdmin(myComponent) + navCtrl.navigate("HomePage") { + popUpTo( + navCtrl.graph.findStartDestination().id + ) { saveState = true } + } + } + ) { Text("撤销") } }else{ @@ -79,47 +92,64 @@ fun DpmPermissions(myDpm: DevicePolicyManager, myComponent: ComponentName, myCon ) { Column { Text(text = "Device Owner", style = MaterialTheme.typography.titleLarge) - Text(if(isda){"已激活"}else{"未激活"}) + Text(if(isdo){"已激活"}else{"未激活"}) } if(isdo){ - Button(onClick = {myDpm.clearDeviceOwnerApp("com.binbin.androidowner")}) { + Button( + onClick = { + myDpm.clearDeviceOwnerApp("com.binbin.androidowner") + navCtrl.navigate("HomePage") { + popUpTo( + navCtrl.graph.findStartDestination().id + ) { saveState = true } + } + } + ) { Text("撤销") } } } - if(isdo||isda){Text("注意!在这里撤销权限不会清除配置。比如:被停用的应用会保持停用状态")} - Spacer(Modifier.padding(5.dp)) - if(!isda){ - SelectionContainer { - Text("dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver") + Column( + horizontalAlignment = Alignment.Start + ) { + if(isdo||isda){Text("注意!在这里撤销权限不会清除配置。比如:被停用的应用会保持停用状态")} + Spacer(Modifier.padding(5.dp)) + if(!isda){ + Text("你可以在adb shell中使用以下命令激活Device Admin") + SelectionContainer { + Text("dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver") + } + Text("或者进入设置 -> 安全 -> 更多安全设置 -> 设备管理应用 -> Android Owner") } - } - if(!isdo){ - SelectionContainer { - Text("dpm set-device-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver") + if(!isdo){ + Text("你可以在adb shell中使用以下命令激活Device Owner") + SelectionContainer { + Text("dpm set-device-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver") + } + if(!isda){ + Text("使用此命令也会激活Device Admin") + } } - } - if(isdo){ - var lockScrInfo by remember { mutableStateOf("") } - TextField(value = lockScrInfo, onValueChange = { lockScrInfo= it}, label = { Text("锁屏信息") }) - Spacer(Modifier.padding(5.dp)) - Button(onClick = {myDpm.setDeviceOwnerLockScreenInfo(myComponent,lockScrInfo)}) { - Text("设置锁屏DeviceOwner信息") + if(isdo){ + var lockScrInfo by remember { mutableStateOf("") } + TextField(value = lockScrInfo, onValueChange = { lockScrInfo= it}, label = { Text("锁屏信息") }) + Spacer(Modifier.padding(5.dp)) + Button(onClick = {myDpm.setDeviceOwnerLockScreenInfo(myComponent,lockScrInfo)}) { + Text("设置锁屏DeviceOwner信息") + } } } + } } fun ActivateDeviceAdmin(myDpm: DevicePolicyManager,myComponent: ComponentName,myContext: Context){ - if (!myDpm.isAdminActive(myComponent)) { - val intent = Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN) - intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, myComponent) - intent.putExtra( - DevicePolicyManager.EXTRA_ADD_EXPLANATION, - "在这里激活Android Owner" - ) - startActivity(myContext,intent,null) - } else { - Toast.makeText(myContext, "已经激活", Toast.LENGTH_SHORT).show() - } + val intent = Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN) + intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, myComponent) + intent.putExtra( + DevicePolicyManager.EXTRA_ADD_EXPLANATION, + "在这里激活Android Owner" + ) + intent.setFlags(FLAG_ACTIVITY_NEW_TASK) + startActivity(myContext,intent,null) } diff --git a/app/src/main/java/com/binbin/androidowner/Security.kt b/app/src/main/java/com/binbin/androidowner/Security.kt new file mode 100644 index 0000000..d277653 --- /dev/null +++ b/app/src/main/java/com/binbin/androidowner/Security.kt @@ -0,0 +1,25 @@ +package com.binbin.androidowner + +import android.app.admin.DevicePolicyManager +import android.content.ComponentName +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable + +@Composable +fun Security(myDpm:DevicePolicyManager,myComponent:ComponentName){ + Column { + Button(onClick = {myDpm.clearResetPasswordToken(myComponent)}) { + Text("清除重置密码令牌") + } + Button(onClick = {myDpm.setResetPasswordToken(myComponent, byteArrayOf(32))}) { + Text("设置重置密码令牌") + } + Text("不知道上面两个东西干啥用的") + Button(onClick = {myDpm.resetPassword(null,0)}) { + Text("清除密码") + } + } + +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8d5942f..ae647a7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -40,4 +40,11 @@ 自动设置时间 自动设置时区 备份服务 + 隐藏 + 停用 + 防卸载 + 如果隐藏,有可能是没安装 + 有时候不能用 + 禁止用户控制 + 阻止清除应用数据和缓存> \ No newline at end of file