mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 19:15:58 +00:00
361 lines
16 KiB
Kotlin
361 lines
16 KiB
Kotlin
package com.binbin.androidowner
|
|
|
|
import android.annotation.SuppressLint
|
|
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.foundation.*
|
|
import androidx.compose.foundation.layout.*
|
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
import androidx.compose.material.icons.Icons
|
|
import androidx.compose.material.icons.outlined.ArrowBack
|
|
import androidx.compose.material.icons.outlined.Home
|
|
import androidx.compose.material3.*
|
|
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.graphics.Color
|
|
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.unit.dp
|
|
import androidx.core.view.WindowCompat
|
|
import androidx.navigation.NavHostController
|
|
import androidx.navigation.compose.NavHost
|
|
import androidx.navigation.compose.composable
|
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
|
import androidx.navigation.compose.rememberNavController
|
|
import com.binbin.androidowner.ui.theme.AndroidOwnerTheme
|
|
import java.io.FileNotFoundException
|
|
import java.io.IOException
|
|
import java.io.InputStream
|
|
|
|
|
|
lateinit var getCaCert: ActivityResultLauncher<Intent>
|
|
lateinit var createUser:ActivityResultLauncher<Intent>
|
|
lateinit var createManagedProfile:ActivityResultLauncher<Intent>
|
|
lateinit var getApk:ActivityResultLauncher<Intent>
|
|
lateinit var getUserIcon:ActivityResultLauncher<Intent>
|
|
var userIconUri:Uri? = null
|
|
var apkUri: Uri? = null
|
|
var caCert = byteArrayOf()
|
|
|
|
@ExperimentalMaterial3Api
|
|
class MainActivity : ComponentActivity() {
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
|
super.onCreate(savedInstanceState)
|
|
getUserIcon = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
userIconUri = it.data?.data
|
|
if(userIconUri==null){ Toast.makeText(applicationContext, "空URI", Toast.LENGTH_SHORT).show() }
|
|
}
|
|
getApk = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
apkUri = it.data?.data
|
|
if(apkUri==null){ Toast.makeText(applicationContext, "空URI", Toast.LENGTH_SHORT).show() }
|
|
}
|
|
getCaCert = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
uriToStream(applicationContext,it.data?.data){stream->
|
|
caCert = stream.readBytes()
|
|
if(caCert.size>50000){ Toast.makeText(applicationContext, "太大了", Toast.LENGTH_SHORT).show(); caCert = byteArrayOf() }
|
|
}
|
|
}
|
|
createUser = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
when(it.resultCode){
|
|
Activity.RESULT_OK->Toast.makeText(applicationContext, "成功", Toast.LENGTH_SHORT).show()
|
|
Activity.RESULT_CANCELED->Toast.makeText(applicationContext, "用户太多了", Toast.LENGTH_SHORT).show()
|
|
UserManager.USER_CREATION_FAILED_NOT_PERMITTED->Toast.makeText(applicationContext, "不是管理员用户", Toast.LENGTH_SHORT).show()
|
|
UserManager.USER_CREATION_FAILED_NO_MORE_USERS->Toast.makeText(applicationContext, "用户太多了", Toast.LENGTH_SHORT).show()
|
|
else->Toast.makeText(applicationContext, "创建用户结果未知", Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
createManagedProfile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
if(it.resultCode==Activity.RESULT_CANCELED){Toast.makeText(applicationContext, "用户已取消", Toast.LENGTH_SHORT).show()}
|
|
}
|
|
setContent {
|
|
AndroidOwnerTheme {
|
|
MyScaffold()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@ExperimentalMaterial3Api
|
|
@Composable
|
|
fun MyScaffold(){
|
|
val focusMgr = LocalFocusManager.current
|
|
val navCtrl = rememberNavController()
|
|
val backStackEntry by navCtrl.currentBackStackEntryAsState()
|
|
val myContext = LocalContext.current
|
|
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
|
val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
|
|
val topBarNameMap = mapOf(
|
|
"HomePage" to R.string.app_name,
|
|
"DeviceControl" to R.string.device_ctrl,
|
|
"Network" to R.string.network,
|
|
"ManagedProfile" to R.string.work_profile,
|
|
"Permissions" to R.string.permission,
|
|
"UserManage" to R.string.user_manage,
|
|
"ApplicationManage" to R.string.app_manage,
|
|
"UserRestriction" to R.string.user_restrict,
|
|
"Password" to R.string.password,
|
|
"AppSetting" to R.string.setting
|
|
)
|
|
val topBarName = topBarNameMap[backStackEntry?.destination?.route]?: R.string.app_name
|
|
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
|
|
Scaffold(
|
|
topBar = {
|
|
if(!sharedPref.getBoolean("isWear",false)){
|
|
TopAppBar(
|
|
title = { Text(text = stringResource(topBarName) , color = MaterialTheme.colorScheme.onSurface) },
|
|
colors = TopAppBarDefaults.topAppBarColors(
|
|
containerColor = MaterialTheme.colorScheme.surface
|
|
),
|
|
navigationIcon = {
|
|
if(topBarName!=R.string.app_name){
|
|
Icon(
|
|
imageVector = Icons.Outlined.ArrowBack,
|
|
contentDescription = "Back",
|
|
modifier = Modifier
|
|
.padding(horizontal = 6.dp)
|
|
.clip(RoundedCornerShape(50))
|
|
.clickable{ navCtrl.navigateUp(); focusMgr.clearFocus() }
|
|
.padding(5.dp)
|
|
)
|
|
}
|
|
}
|
|
)
|
|
}
|
|
},
|
|
floatingActionButton = {
|
|
if(sharedPref.getBoolean("isWear",false)&&topBarName!=R.string.app_name){
|
|
FloatingActionButton(
|
|
onClick = {
|
|
focusMgr.clearFocus()
|
|
navCtrl.navigateUp()
|
|
},
|
|
modifier = Modifier.size(35.dp),
|
|
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
|
|
contentColor = MaterialTheme.colorScheme.tertiary
|
|
) {
|
|
Icon(imageVector = Icons.Outlined.Home, contentDescription = null)
|
|
}
|
|
}
|
|
}
|
|
) {
|
|
val profileInited = sharedPref.getBoolean("ManagedProfileActivated",false)
|
|
var inited by remember{mutableStateOf(false)}
|
|
val jumpToActivateProfile = !profileInited&&isProfileOwner(myDpm)&&(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isManagedProfile(myComponent)))
|
|
NavHost(
|
|
navController = navCtrl,
|
|
startDestination = "HomePage",
|
|
modifier = Modifier.padding(top = it.calculateTopPadding()).imePadding()
|
|
){
|
|
composable(route = "HomePage", content = { HomePage(navCtrl)})
|
|
composable(route = "DeviceControl", content = { DeviceControl()})
|
|
composable(route = "ManagedProfile", content = {ManagedProfile()})
|
|
composable(route = "Permissions", content = { DpmPermissions(navCtrl)})
|
|
composable(route = "ApplicationManage", content = { ApplicationManage()})
|
|
composable(route = "UserRestriction", content = { UserRestriction()})
|
|
composable(route = "UserManage", content = { UserManage()})
|
|
composable(route = "Password", content = { Password()})
|
|
composable(route = "AppSetting", content = { AppSetting(navCtrl)})
|
|
composable(route = "Network", content = {Network()})
|
|
composable(route = "ActivateManagedProfile", content = {ActivateManagedProfile(navCtrl)})
|
|
}
|
|
if(!inited&&jumpToActivateProfile){navCtrl.navigate("ActivateManagedProfile");inited=true}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun HomePage(navCtrl:NavHostController){
|
|
val myContext = LocalContext.current
|
|
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
|
val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
|
|
val activateType =
|
|
if(isDeviceOwner(myDpm)){"Device Owner"}
|
|
else if(isProfileOwner(myDpm)){if(VERSION.SDK_INT>=24&&myDpm.isManagedProfile(myComponent)){"工作资料"}else{"Profile Owner"}}
|
|
else if(myDpm.isAdminActive(myComponent)){"Device Admin"}
|
|
else{""}
|
|
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
|
|
val isWear = sharedPref.getBoolean("isWear",false)
|
|
caCert = byteArrayOf()
|
|
Column(modifier = Modifier.verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally) {
|
|
if(isWear){ Spacer(Modifier.padding(vertical = 3.dp)) }
|
|
Row(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.padding(vertical = if (!isWear) { 5.dp } else { 2.dp }, horizontal = if (!isWear) { 8.dp } else { 4.dp })
|
|
.clip(RoundedCornerShape(15))
|
|
.background(color = MaterialTheme.colorScheme.tertiaryContainer.copy(alpha = 0.7F))
|
|
.clickable(onClick = { navCtrl.navigate("Permissions") })
|
|
.padding(
|
|
horizontal = 5.dp,
|
|
vertical = if (!isWear) { 14.dp } else { 2.dp }
|
|
),
|
|
verticalAlignment = Alignment.CenterVertically
|
|
) {
|
|
Icon(
|
|
painter = painterResource(if(myDpm.isAdminActive(myComponent)){ R.drawable.check_fill0 }else{ R.drawable.block_fill0 }),
|
|
contentDescription = null,
|
|
modifier = Modifier.padding(horizontal = if(!isWear){10.dp}else{6.dp}),
|
|
tint = MaterialTheme.colorScheme.tertiary
|
|
)
|
|
Column {
|
|
Text(
|
|
text = if(isDeviceOwner(myDpm)||myDpm.isAdminActive(myComponent)||isProfileOwner(myDpm)){"已激活"}else{"未激活"},
|
|
style = typography.headlineSmall,
|
|
color = MaterialTheme.colorScheme.onTertiaryContainer
|
|
)
|
|
if(activateType!=""){ Text(activateType) }
|
|
}
|
|
}
|
|
HomePageItem(R.string.device_ctrl, R.drawable.mobile_phone_fill0, "DeviceControl", navCtrl)
|
|
if(VERSION.SDK_INT>=24){HomePageItem(R.string.network, R.drawable.wifi_fill0, "Network",navCtrl)}
|
|
HomePageItem(R.string.work_profile, R.drawable.work_fill0, "ManagedProfile",navCtrl)
|
|
HomePageItem(R.string.app_manage, R.drawable.apps_fill0, "ApplicationManage", navCtrl)
|
|
HomePageItem(R.string.user_restrict, R.drawable.manage_accounts_fill0, "UserRestriction", navCtrl)
|
|
HomePageItem(R.string.user_manage,R.drawable.account_circle_fill0,"UserManage",navCtrl)
|
|
HomePageItem(R.string.password, R.drawable.password_fill0, "Password",navCtrl)
|
|
HomePageItem(R.string.setting, R.drawable.info_fill0, "AppSetting",navCtrl)
|
|
Spacer(Modifier.padding(vertical = 20.dp))
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun HomePageItem(name:Int, imgVector:Int, navTo:String, myNav:NavHostController){
|
|
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
|
|
val isWear = sharedPref.getBoolean("isWear", false)
|
|
Row(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.padding(vertical = if (!isWear) { 4.dp } else { 2.dp }, horizontal = if (!isWear) { 7.dp } else { 4.dp })
|
|
.clip(RoundedCornerShape(15))
|
|
.background(color = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.7F))
|
|
.clickable(onClick = { myNav.navigate(navTo) })
|
|
.padding(vertical = if(isWear){6.dp}else{10.dp}, horizontal = 6.dp),
|
|
verticalAlignment = Alignment.CenterVertically
|
|
|
|
) {
|
|
Icon(
|
|
painter = painterResource(imgVector),
|
|
contentDescription = null,
|
|
modifier = Modifier.padding(horizontal = if(!sharedPref.getBoolean("isWear",false)){12.dp}else{6.dp}),
|
|
tint = MaterialTheme.colorScheme.primary
|
|
)
|
|
Column {
|
|
Text(
|
|
text = stringResource(name),
|
|
style = typography.headlineSmall,
|
|
color = MaterialTheme.colorScheme.onPrimaryContainer
|
|
)
|
|
Spacer(Modifier.padding(top = 2.dp))
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun RadioButtonItem(
|
|
text:String,
|
|
selected:()->Boolean,
|
|
operation:()->Unit,
|
|
textColor:Color = MaterialTheme.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 = MaterialTheme.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
|
|
@Stable
|
|
fun sections(bgColor:Color=MaterialTheme.colorScheme.primaryContainer):Modifier{
|
|
val backgroundColor = if(isSystemInDarkTheme()){bgColor.copy(0.3F)}else{bgColor.copy(0.8F)}
|
|
return if(!LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE).getBoolean("isWear",false)){
|
|
Modifier
|
|
.fillMaxWidth()
|
|
.padding(horizontal = 8.dp, vertical = 4.dp)
|
|
.clip(RoundedCornerShape(14.dp))
|
|
.background(color = backgroundColor)
|
|
.padding(vertical = 10.dp, horizontal = 10.dp)
|
|
}else{
|
|
Modifier
|
|
.fillMaxWidth()
|
|
.padding(horizontal = 3.dp, vertical = 3.dp)
|
|
.clip(RoundedCornerShape(10.dp))
|
|
.background(color = backgroundColor)
|
|
.padding(vertical = 2.dp, horizontal = 3.dp)
|
|
}
|
|
}
|
|
|
|
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() }
|
|
}
|