From d4de1eba75ff3ee40fa4615a638bf781558c9017 Mon Sep 17 00:00:00 2001 From: BinTianqi Date: Fri, 19 Jul 2024 09:09:10 +0800 Subject: [PATCH] hide Wipe data by default, add Delete work profile close #53 --- .../java/com/bintianqi/owndroid/Setting.kt | 14 +++ .../bintianqi/owndroid/dpm/ManagedProfile.kt | 94 ++++++++++++++++++- .../bintianqi/owndroid/dpm/SystemManager.kt | 14 +-- app/src/main/res/values-tr/strings.xml | 8 +- app/src/main/res/values-zh-rCN/strings.xml | 3 + app/src/main/res/values/strings.xml | 9 +- 6 files changed, 126 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/bintianqi/owndroid/Setting.kt b/app/src/main/java/com/bintianqi/owndroid/Setting.kt index e0ed1bc..c9da38a 100644 --- a/app/src/main/java/com/bintianqi/owndroid/Setting.kt +++ b/app/src/main/java/com/bintianqi/owndroid/Setting.kt @@ -44,6 +44,7 @@ fun AppSetting(navCtrl:NavHostController, materialYou: MutableState, bl modifier = Modifier.padding(top = it.calculateTopPadding()) ) { composable(route = "Home") { Home(localNavCtrl) } + composable(route = "Options") { Options() } composable(route = "Theme") { ThemeSettings(materialYou, blackTheme) } composable(route = "Auth") { AuthSettings() } composable(route = "Automation") { Automation() } @@ -55,6 +56,7 @@ fun AppSetting(navCtrl:NavHostController, materialYou: MutableState, bl @Composable private fun Home(navCtrl: NavHostController) { Column(modifier = Modifier.fillMaxSize()) { + SubPageItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Options") } SubPageItem(R.string.theme, "", R.drawable.format_paint_fill0) { navCtrl.navigate("Theme") } SubPageItem(R.string.security, "", R.drawable.lock_fill0) { navCtrl.navigate("Auth") } SubPageItem(R.string.automation_api, "", R.drawable.apps_fill0) { navCtrl.navigate("Automation") } @@ -62,6 +64,18 @@ private fun Home(navCtrl: NavHostController) { } } +@Composable +private fun Options() { + val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE) + Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { + SwitchItem( + R.string.show_dangerous_features, "", R.drawable.warning_fill0, + { sharedPref.getBoolean("dangerous_features", false) }, + { sharedPref.edit().putBoolean("dangerous_features", it).apply() } + ) + } +} + @Composable private fun ThemeSettings(materialYou:MutableState, blackTheme:MutableState) { val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE) diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt index 39a5ae8..82ac61e 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt @@ -11,11 +11,15 @@ import android.app.admin.DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT import android.app.admin.DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED import android.app.admin.DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED import android.app.admin.DevicePolicyManager.PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT +import android.app.admin.DevicePolicyManager.WIPE_EUICC +import android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE +import android.app.admin.DevicePolicyManager.WIPE_SILENTLY 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.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -26,13 +30,17 @@ 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.AlertDialog import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -80,6 +88,7 @@ fun ManagedProfile(navCtrl: NavHostController) { composable(route = "CreateWorkProfile") { CreateWorkProfile() } composable(route = "SuspendPersonalApp") { SuspendPersonalApp() } composable(route = "IntentFilter") { IntentFilter() } + composable(route = "DeleteWorkProfile") { DeleteWorkProfile() } } } } @@ -97,7 +106,7 @@ private fun Home(navCtrl: NavHostController) { style = typography.headlineLarge, modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 15.dp) ) - if(VERSION.SDK_INT >= 30&&isProfileOwner(dpm) && dpm.isManagedProfile(receiver)) { + if(VERSION.SDK_INT >= 30 && isProfileOwner(dpm) && dpm.isManagedProfile(receiver)) { SubPageItem(R.string.org_owned_work_profile, "", R.drawable.corporate_fare_fill0) { navCtrl.navigate("OrgOwnedWorkProfile") } } if(VERSION.SDK_INT<24 || (VERSION.SDK_INT>=24 && dpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE))) { @@ -106,9 +115,12 @@ private fun Home(navCtrl: NavHostController) { if(dpm.isOrgProfile(receiver)) { SubPageItem(R.string.suspend_personal_app, "", R.drawable.block_fill0) { navCtrl.navigate("SuspendPersonalApp") } } - if(isProfileOwner(dpm) && (VERSION.SDK_INT<24 || (VERSION.SDK_INT>=24 && dpm.isManagedProfile(receiver)))) { + if(isProfileOwner(dpm) && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)))) { SubPageItem(R.string.intent_filter, "", R.drawable.filter_alt_fill0) { navCtrl.navigate("IntentFilter") } } + if(isProfileOwner(dpm) && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)))) { + SubPageItem(R.string.delete_work_profile, "", R.drawable.delete_forever_fill0) { navCtrl.navigate("DeleteWorkProfile") } + } Spacer(Modifier.padding(vertical = 30.dp)) } } @@ -265,3 +277,81 @@ private fun IntentFilter() { } } } + +@Composable +private fun DeleteWorkProfile() { + val context = LocalContext.current + val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager + val focusMgr = LocalFocusManager.current + var warning by remember { mutableStateOf(false) } + var externalStorage by remember { mutableStateOf(false) } + var euicc by remember { mutableStateOf(false) } + var silent by remember { mutableStateOf(false) } + var reason by remember { mutableStateOf("") } + Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { + Spacer(Modifier.padding(vertical = 10.dp)) + Text( + text = stringResource(R.string.delete_work_profile), + style = typography.headlineLarge, + modifier = Modifier.padding(6.dp),color = colorScheme.error + ) + Spacer(Modifier.padding(vertical = 5.dp)) + CheckBoxItem(stringResource(R.string.wipe_external_storage), externalStorage, { externalStorage = it }) + if(VERSION.SDK_INT >= 28) { CheckBoxItem(stringResource(R.string.wipe_euicc), euicc, { euicc = it }) } + if(VERSION.SDK_INT >= 29) { CheckBoxItem(stringResource(R.string.wipe_silently), silent, { silent = it }) } + AnimatedVisibility(!silent && VERSION.SDK_INT >= 28) { + OutlinedTextField( + value = reason, onValueChange = { reason = it }, + label = { Text(stringResource(R.string.reason)) }, + modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp) + ) + } + Spacer(Modifier.padding(vertical = 5.dp)) + Button( + onClick = { + focusMgr.clearFocus() + warning = true + }, + colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError), + modifier = Modifier.fillMaxWidth() + ) { + Text(stringResource(R.string.delete)) + } + Spacer(Modifier.padding(vertical = 30.dp)) + } + if(warning) { + LaunchedEffect(Unit) { silent = reason == "" } + AlertDialog( + title = { + Text(text = stringResource(R.string.warning), color = colorScheme.error) + }, + text = { + Text(text = stringResource(R.string.wipe_work_profile_warning), color = colorScheme.error) + }, + onDismissRequest = { warning = false }, + confirmButton = { + TextButton( + onClick = { + var flag = 0 + if(externalStorage) { flag += WIPE_EXTERNAL_STORAGE } + if(euicc && VERSION.SDK_INT >= 28) { flag += WIPE_EUICC } + if(silent && VERSION.SDK_INT >= 29) { flag += WIPE_SILENTLY } + if(VERSION.SDK_INT >= 28 && !silent) { + dpm.wipeData(flag, reason) + } else { + dpm.wipeData(flag) + } + }, + colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error) + ) { + Text(stringResource(R.string.confirm)) + } + }, + dismissButton = { + TextButton(onClick = { warning = false }) { + Text(stringResource(R.string.cancel)) + } + } + ) + } +} diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt index 6a7d4dd..0045898 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt @@ -176,6 +176,8 @@ private fun Home(navCtrl: NavHostController, scrollState: ScrollState, rebootDia val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context, Receiver::class.java) + val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE) + val dangerousFeatures = sharedPref.getBoolean("dangerous_features", false) Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) { Text( text = stringResource(R.string.system_manage), @@ -223,7 +225,7 @@ private fun Home(navCtrl: NavHostController, scrollState: ScrollState, rebootDia if(VERSION.SDK_INT >= 30 && (isDeviceOwner(dpm) || dpm.isOrgProfile(receiver))) { SubPageItem(R.string.frp_policy, "", R.drawable.device_reset_fill0) { navCtrl.navigate("FRP") } } - if(dpm.isAdminActive(receiver)) { + if(dangerousFeatures && dpm.isAdminActive(receiver) && !(VERSION.SDK_INT >= 24 && isProfileOwner(dpm) && dpm.isManagedProfile(receiver))) { SubPageItem(R.string.wipe_data, "", R.drawable.device_reset_fill0) { navCtrl.navigate("WipeData") } } Spacer(Modifier.padding(vertical = 30.dp)) @@ -1131,10 +1133,6 @@ private fun WipeData() { Text("WipeDevice") } } - Spacer(Modifier.padding(vertical = 5.dp)) - if(VERSION.SDK_INT >= 24 && isProfileOwner(dpm) && dpm.isManagedProfile(receiver)) { - Information{ Text(text = stringResource(R.string.will_delete_work_profile)) } - } Spacer(Modifier.padding(vertical = 30.dp)) } if(warning) { @@ -1144,11 +1142,7 @@ private fun WipeData() { Text(text = stringResource(R.string.warning), color = colorScheme.error) }, text = { - Text(text = stringResource( - if(VERSION.SDK_INT >= 24 && isProfileOwner(dpm) && dpm.isManagedProfile(receiver)) R.string.wipe_work_profile_warning - else R.string.wipe_data_warning), - color = colorScheme.error - ) + Text(text = stringResource(R.string.wipe_data_warning), color = colorScheme.error) }, onDismissRequest = { warning = false }, confirmButton = { diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index b9d15eb..945bc3d 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -55,6 +55,7 @@ Politika Kullan Hesap Warning + Delete Etkinleştirmek İçin Tıklayın @@ -181,7 +182,7 @@ Sessizce sil Çalışma profili silinecek All data on your device will be ERASED - Your work profile will be REMOVED + Your work profile will be DELETED Şifreleme durumu: FRP politikası Fabrika ayarlarına sıfırlama koruma politikası @@ -276,6 +277,7 @@ Kuruluş Kimliği Uzunluk 6 ile 64 karakter arasında olmalıdır Bunu ayarladıktan sonra cihaz spesifik Kimlik alabilirsiniz. + Delete work profile Uygulama yöneticisi @@ -527,6 +529,7 @@ Ayarlar + Show dangerous features Material You rengi Android 12+ Hakkında @@ -552,6 +555,9 @@ Depolamayı temizle Depolama başarıyla temizlendi\nUygulama kapanacak + Automation API + Debug mode + Harici depolamayı oku Harici depolamaya yaz diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index cf45513..8fdff33 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -52,6 +52,7 @@ 使用策略 账户 警告 + 删除 点击以激活 @@ -271,6 +272,7 @@ 组织ID 长度应在6~64个字符之间 设置组织ID后才能获取设备唯一标识码 + 删除工作资料 应用管理 @@ -518,6 +520,7 @@ 设置 + 显示危险功能 Material you 颜色 安卓12+ 关于 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d3e0c89..8155a86 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -55,6 +55,7 @@ Use policy Account Warning + Delete Click to activate @@ -110,8 +111,8 @@ dpm set-active-admin com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver Permission granted (Shell) Permission granted (Root) - Activate profile owner - Activate device owner + Activate Profile owner + Activate Device owner Activate organization-owned work profile Shizuku service disconnected Invalid binder @@ -186,7 +187,7 @@ Wipe silently Work profile will be deleted. All data on your device will be ERASED - Your work profile will be REMOVED + Your work profile will be DELETED Encrypt status: FRP policy Factory reset protection policy @@ -285,6 +286,7 @@ Organization ID The length should be between 6~64 characters You can get device specific ID after set this. + Delete work profile App manager @@ -534,6 +536,7 @@ Settings + Show dangerous features Material you color Android 12+ About