diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c097f3a..1bcefe2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "com.binbin.androidowner" minSdk = 21 targetSdk = 34 - versionCode = 11 - versionName = "2.4" + versionCode = 12 + versionName = "2.5" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2dd96ac..f4e968e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,6 +15,7 @@ + =24&&isDeviceOwner(myDpm)){ + if(VERSION.SDK_INT>=24&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){ val isSuspended: Boolean = try{ myDpm.isPackageSuspended(myComponent,pkgName) } catch(e:NameNotFoundException){ false } catch(w:NameNotFoundException){ false } @@ -472,7 +472,6 @@ private fun AppManageItem( val myDpm = LocalContext.current.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val focusMgr = LocalFocusManager.current var isEnabled by remember{ mutableStateOf(false) } - if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)){ isEnabled = getMethod() } val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE) if(!sharedPref.getBoolean("isWear",false)){ Row( diff --git a/app/src/main/java/com/binbin/androidowner/DeviceControl.kt b/app/src/main/java/com/binbin/androidowner/DeviceControl.kt index 9b225b5..710ae27 100644 --- a/app/src/main/java/com/binbin/androidowner/DeviceControl.kt +++ b/app/src/main/java/com/binbin/androidowner/DeviceControl.kt @@ -492,7 +492,7 @@ fun DeviceControl(){ Text(text = "清除数据",style = typography.titleLarge,modifier = Modifier.padding(6.dp),color = colorScheme.onErrorContainer) RadioButtonItem("默认",{flag==0},{flag=0}, colorScheme.onErrorContainer) RadioButtonItem("WIPE_EXTERNAL_STORAGE",{flag==WIPE_EXTERNAL_STORAGE},{flag=WIPE_EXTERNAL_STORAGE}, colorScheme.onErrorContainer) - if(VERSION.SDK_INT>=22){ + if(VERSION.SDK_INT>=22&&isDeviceOwner(myDpm)){ RadioButtonItem("WIPE_RESET_PROTECTION_DATA",{flag==WIPE_RESET_PROTECTION_DATA},{flag=WIPE_RESET_PROTECTION_DATA}, colorScheme.onErrorContainer) } if(VERSION.SDK_INT>=28){ RadioButtonItem("WIPE_EUICC",{flag==WIPE_EUICC},{flag=WIPE_EUICC}, colorScheme.onErrorContainer) } @@ -533,6 +533,9 @@ fun DeviceControl(){ } } } + if(VERSION.SDK_INT>=24&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){ + Text("将会删除工作资料") + } } Spacer(Modifier.padding(vertical = 30.dp)) } diff --git a/app/src/main/java/com/binbin/androidowner/MainActivity.kt b/app/src/main/java/com/binbin/androidowner/MainActivity.kt index 05995e4..9f052ed 100644 --- a/app/src/main/java/com/binbin/androidowner/MainActivity.kt +++ b/app/src/main/java/com/binbin/androidowner/MainActivity.kt @@ -23,6 +23,7 @@ import androidx.compose.material.icons.outlined.Home import androidx.compose.material3.* import androidx.compose.material3.MaterialTheme.typography import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -100,6 +101,7 @@ fun MyScaffold(){ "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, @@ -160,6 +162,7 @@ fun MyScaffold(){ ){ composable(route = "HomePage", content = { HomePage(navCtrl)}) composable(route = "DeviceControl", content = { DeviceControl()}) + composable(route = "ManagedProfile", content = {ManagedProfile(navCtrl)}) composable(route = "Permissions", content = { DpmPermissions(navCtrl)}) composable(route = "ApplicationManage", content = { ApplicationManage()}) composable(route = "UserRestriction", content = { UserRestriction()}) @@ -216,6 +219,7 @@ fun HomePage(navCtrl:NavHostController){ } HomePageItem(R.string.device_ctrl, R.drawable.mobile_phone_fill0, "DeviceControl", navCtrl) if(VERSION.SDK_INT>=26){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) @@ -311,6 +315,7 @@ fun isProfileOwner(dpm:DevicePolicyManager): Boolean { @SuppressLint("ModifierFactoryExtensionFunction", "ComposableModifierFactory") @Composable +@Stable fun sections(bgColor:Color=MaterialTheme.colorScheme.primaryContainer):Modifier{ val backgroundColor = if(isSystemInDarkTheme()){bgColor.copy(0.4F)}else{bgColor.copy(0.6F)} return if(!LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE).getBoolean("isWear",false)){ diff --git a/app/src/main/java/com/binbin/androidowner/ManagedProfile.kt b/app/src/main/java/com/binbin/androidowner/ManagedProfile.kt new file mode 100644 index 0000000..dee21e6 --- /dev/null +++ b/app/src/main/java/com/binbin/androidowner/ManagedProfile.kt @@ -0,0 +1,122 @@ +package com.binbin.androidowner + +import android.app.admin.DevicePolicyManager +import android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.os.Build.VERSION +import android.os.Bundle +import android.os.Parcelable +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +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.navigation.NavHostController + +@Composable +fun ManagedProfile(navCtrl:NavHostController){ + 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} + Column(modifier = Modifier.verticalScroll(rememberScrollState())){ + Column(modifier = sections()){ + Text(text = "信息", style = typography.titleLarge) + if(VERSION.SDK_INT>=30){ + Text(text = "由组织拥有的工作资料:${myDpm.isOrganizationOwnedDeviceWithManagedProfile}", style = bodyTextStyle) + } + } + Column(modifier = sections()) { + Text(text = "工作资料", style = typography.titleLarge) + if(VERSION.SDK_INT>=24){ + if(isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){ + Text(text = "已是工作资料") + }else{ + Text(text = "可以创建工作资料:${myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)}", style = bodyTextStyle) + } + } + if(isDeviceOwner(myDpm)){ + Text(text = "Device owner不能创建工作资料", style = bodyTextStyle) + } + if(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE))){ + var skipEncrypt by remember{mutableStateOf(false)} + if(VERSION.SDK_INT>=24){CheckBoxItem("跳过加密",{skipEncrypt},{skipEncrypt=!skipEncrypt})} + Button( + onClick = { + val intent = Intent(ACTION_PROVISION_MANAGED_PROFILE) + if(VERSION.SDK_INT>=23){ + intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,myComponent) + }else{ + intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,"com.binbin.androidowner") + } + if(VERSION.SDK_INT>=24){intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION,skipEncrypt)} + if(VERSION.SDK_INT>=33){intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_ALLOW_OFFLINE,true)} + createManagedProfile.launch(intent) + }, + modifier = Modifier.fillMaxWidth() + ) { + Text("创建") + } + } + if(isProfileOwner(myDpm)&&(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isManagedProfile(myComponent)))){ + Button( + onClick = { + myDpm.setProfileEnabled(myComponent) + navCtrl.navigateUp() + Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show() + }, + modifier = Modifier.fillMaxWidth() + ) { + Text(text = "激活") + } + } + } + + if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile){ + Column(modifier = sections()){ + var time by remember{mutableStateOf("")} + time = myDpm.getManagedProfileMaximumTimeOff(myComponent).toString() + Text(text = "资料关闭时间", style = typography.titleLarge) + Text(text = "工作资料处于关闭状态的时间达到该限制后会停用个人应用,0为无限制(单位:毫秒)", style = bodyTextStyle) + TextField( + value = time, onValueChange = {time=it}, modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp), + label = {Text("时间(ms)")}, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}) + ) + Text(text = "不能少于72小时") + Button( + onClick = { + myDpm.setManagedProfileMaximumTimeOff(myComponent,time.toLong()) + Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show() + } + ) { + Text("应用") + } + } + } + + Spacer(Modifier.padding(vertical = 30.dp)) + } +} diff --git a/app/src/main/java/com/binbin/androidowner/Permissions.kt b/app/src/main/java/com/binbin/androidowner/Permissions.kt index a849181..74b6dea 100644 --- a/app/src/main/java/com/binbin/androidowner/Permissions.kt +++ b/app/src/main/java/com/binbin/androidowner/Permissions.kt @@ -28,6 +28,7 @@ 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.compose.ui.unit.sp import androidx.core.content.ContextCompat.startActivity @@ -160,6 +161,7 @@ fun DpmPermissions(navCtrl:NavHostController){ if(!isda){ Text(text = "使用此命令也会激活Device Admin", style = bodyTextStyle) } + Text(text = "成为Device owner后将不能新建工作资料", style = bodyTextStyle) } } if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)||myDpm.isAdminActive(myComponent)){ @@ -175,8 +177,6 @@ fun DpmPermissions(navCtrl:NavHostController){ modifier = sections() ) { Text(text = "设备信息", style = typography.titleLarge,color = titleColor) - val orgDevice = myDpm.isOrganizationOwnedDeviceWithManagedProfile - Text("由组织拥有的受管理资料设备:$orgDevice",style=bodyTextStyle) if(VERSION.SDK_INT>=34&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){ val financed = myDpm.isDeviceFinanced Text("企业资产 : $financed",style=bodyTextStyle) @@ -186,9 +186,7 @@ fun DpmPermissions(navCtrl:NavHostController){ Text("设备策略管理器角色:${if(dpmRole==null){"null"}else{""}}",style=bodyTextStyle) if(dpmRole!=null){ Row(modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState())){ - SelectionContainer { - Text(dpmRole) - } + SelectionContainer { Text(dpmRole) } } } } @@ -234,6 +232,33 @@ fun DpmPermissions(navCtrl:NavHostController){ } } } + + if((VERSION.SDK_INT>=26&&isDeviceOwner(myDpm))||(VERSION.SDK_INT>=24&&isProfileOwner(myDpm))){ + Column(modifier = sections()){ + var orgName by remember{ + mutableStateOf( + if(myDpm.getOrganizationName(myComponent).toString()=="null"){ "" }else{ myDpm.getOrganizationName(myComponent).toString() } + ) + } + Text(text = "组织名称", style = typography.titleLarge) + TextField( + value = orgName, onValueChange = {orgName=it}, modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp), + label = {Text("组织名称")}, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = {focusManager.clearFocus()}) + ) + Button( + onClick = { + myDpm.setOrganizationName(myComponent,orgName) + Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show() + }, + modifier = Modifier.fillMaxWidth() + ){ + Text("应用") + } + } + } + if(isDeviceOwner(myDpm) || isProfileOwner(myDpm)){ Column(modifier = sections()) { Text(text = "不受控制的账号类型", style = typography.titleLarge,color = titleColor) @@ -267,12 +292,16 @@ fun DpmPermissions(navCtrl:NavHostController){ 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("列表") + Button( + onClick={ + focusManager.clearFocus() + myDpm.setAccountManagementDisabled(myComponent,inputText,true) + noManageAccount=myDpm.accountTypesWithManagementDisabled + refreshList() + }, + modifier = Modifier.fillMaxWidth(0.49f) + ){ + Text("添加") } Button( onClick={focusManager.clearFocus() @@ -282,7 +311,7 @@ fun DpmPermissions(navCtrl:NavHostController){ }, modifier = Modifier.fillMaxWidth(0.96F) ){ - Text("从列表中移除") + Text("移除") } } } @@ -370,7 +399,7 @@ fun DeviceOwnerInfo( fm.clearFocus() Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show() }, - modifier = if(isWear){Modifier.fillMaxWidth(0.48F)}else{Modifier.fillMaxWidth(0.6F)} + modifier = if(isWear){Modifier.fillMaxWidth(0.49F)}else{Modifier.fillMaxWidth(0.6F)} ) { Text(text = "应用") } @@ -381,7 +410,7 @@ fun DeviceOwnerInfo( fm.clearFocus() Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show() }, - modifier = Modifier.fillMaxWidth(0.95F) + modifier = Modifier.fillMaxWidth(0.96F) ) { Text(text = "重置") } diff --git a/app/src/main/java/com/binbin/androidowner/User.kt b/app/src/main/java/com/binbin/androidowner/User.kt index f73c0a2..6fee3b8 100644 --- a/app/src/main/java/com/binbin/androidowner/User.kt +++ b/app/src/main/java/com/binbin/androidowner/User.kt @@ -192,43 +192,6 @@ fun UserManage(navCtrl:NavHostController){ } } - Column(modifier = sections()) { - Text(text = "工作资料", style = typography.titleLarge) - if(VERSION.SDK_INT>=24){Text(text = "可以创建工作资料:${myDpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)}", style = bodyTextStyle)} - if(isDeviceOwner(myDpm)){Text(text = "Device owner不能创建工作资料", style = bodyTextStyle)} - if(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE))){ - var skipEncrypt by remember{mutableStateOf(false)} - if(VERSION.SDK_INT>=24){CheckBoxItem("跳过加密",{skipEncrypt},{skipEncrypt=!skipEncrypt})} - Button( - onClick = { - val intent = Intent(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE) - if(VERSION.SDK_INT>=23){ - intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,myComponent) - }else{ - intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,"com.binbin.androidowner") - } - if(VERSION.SDK_INT>=24){intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION,skipEncrypt)} - if(VERSION.SDK_INT>=33){intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_ALLOW_OFFLINE,true)} - createManagedProfile.launch(intent) - }, - modifier = Modifier.fillMaxWidth() - ) { - Text("创建") - } - } - if(isProfileOwner(myDpm)){ - Button( - onClick = { - myDpm.setProfileEnabled(myComponent) - Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show() - }, - modifier = Modifier.fillMaxWidth() - ) { - Text(text = "启用") - } - } - } - if(VERSION.SDK_INT>=24){ Column(modifier = sections()) { var userName by remember{ mutableStateOf("") } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8033fe6..4c1e85e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -122,4 +122,5 @@ 网络 功能开发中 WiFi锁定 + 工作资料