From 2c1898e4a08265185c8b8554248a87ea4c093bae Mon Sep 17 00:00:00 2001 From: BinTianqi Date: Sun, 17 Nov 2024 10:40:01 +0800 Subject: [PATCH] Set default Affiliation ID while app launch Wipe data warning timer Display profile owner entry in non-system users Fix some UI bugs --- .../com/bintianqi/owndroid/MainActivity.kt | 11 +++- .../bintianqi/owndroid/ManageSpaceActivity.kt | 17 +++-- .../owndroid/dpm/ApplicationManage.kt | 4 +- .../java/com/bintianqi/owndroid/dpm/DPM.kt | 22 +++++++ .../com/bintianqi/owndroid/dpm/Network.kt | 6 +- .../com/bintianqi/owndroid/dpm/Permissions.kt | 20 ++++-- .../bintianqi/owndroid/dpm/ShizukuActivate.kt | 5 +- .../bintianqi/owndroid/dpm/SystemManager.kt | 63 +++++++++++-------- app/src/main/res/values-ru/strings.xml | 2 + app/src/main/res/values-tr/strings.xml | 2 + app/src/main/res/values-zh-rCN/strings.xml | 4 +- app/src/main/res/values/strings.xml | 3 + 12 files changed, 112 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt index 3dd3953..3f55b97 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt @@ -48,6 +48,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.core.view.WindowCompat import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.lifecycleScope import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -66,12 +67,14 @@ import com.bintianqi.owndroid.dpm.getReceiver import com.bintianqi.owndroid.dpm.isDeviceAdmin import com.bintianqi.owndroid.dpm.isDeviceOwner import com.bintianqi.owndroid.dpm.isProfileOwner +import com.bintianqi.owndroid.dpm.setDefaultAffiliationID import com.bintianqi.owndroid.dpm.toggleInstallAppActivity import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.theme.OwnDroidTheme import com.rosan.dhizuku.api.Dhizuku import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch import org.lsposed.hiddenapibypass.HiddenApiBypass import java.util.Locale @@ -85,16 +88,18 @@ class MainActivity : FragmentActivity() { enableEdgeToEdge() WindowCompat.setDecorFitsSystemWindows(window, false) super.onCreate(savedInstanceState) - val sharedPref = applicationContext.getSharedPreferences("data", MODE_PRIVATE) + val context = applicationContext + val sharedPref = context.getSharedPreferences("data", MODE_PRIVATE) if (VERSION.SDK_INT >= 28) HiddenApiBypass.setHiddenApiExemptions("") if(sharedPref.getBoolean("auth", false)) { showAuth.value = true } - val locale = applicationContext.resources?.configuration?.locale + val locale = context.resources?.configuration?.locale zhCN = locale == Locale.SIMPLIFIED_CHINESE || locale == Locale.CHINESE || locale == Locale.CHINA toggleInstallAppActivity() val vm by viewModels() - if(!vm.initialized) vm.initialize(applicationContext) + if(!vm.initialized) vm.initialize(context) + lifecycleScope.launch { setDefaultAffiliationID(context) } setContent { OwnDroidTheme(vm) { Home(vm) diff --git a/app/src/main/java/com/bintianqi/owndroid/ManageSpaceActivity.kt b/app/src/main/java/com/bintianqi/owndroid/ManageSpaceActivity.kt index dc1d2a4..100bbe4 100644 --- a/app/src/main/java/com/bintianqi/owndroid/ManageSpaceActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/ManageSpaceActivity.kt @@ -1,5 +1,6 @@ package com.bintianqi.owndroid +import android.os.Build import android.os.Bundle import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge @@ -11,6 +12,7 @@ import androidx.compose.ui.res.stringResource import androidx.core.view.WindowCompat import androidx.fragment.app.FragmentActivity import com.bintianqi.owndroid.ui.theme.OwnDroidTheme +import kotlin.system.exitProcess class ManageSpaceActivity: FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -32,21 +34,26 @@ class ManageSpaceActivity: FragmentActivity() { }, onDismissRequest = { finish() }, dismissButton = { - if(!protected) TextButton(onClick = { finish() }) { + TextButton(onClick = { finish() }) { Text(stringResource(R.string.cancel)) } }, confirmButton = { - TextButton( + if(!protected) TextButton( onClick = { - if(!protected) { - applicationContext.filesDir.deleteRecursively() + filesDir.deleteRecursively() + cacheDir.deleteRecursively() + codeCacheDir.deleteRecursively() + if(Build.VERSION.SDK_INT >= 24) { + dataDir.resolve("shared_prefs").deleteRecursively() + } else { sharedPref.edit().clear().apply() } finish() + exitProcess(0) } ) { - Text(stringResource(if(protected) R.string.cancel else R.string.confirm)) + Text(stringResource(R.string.confirm)) } } ) diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/ApplicationManage.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/ApplicationManage.kt index e41913b..b604aa7 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/ApplicationManage.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/ApplicationManage.kt @@ -470,9 +470,9 @@ private fun PermissionManage(pkgName: String) { Text(selectedPermission.permission) Spacer(Modifier.padding(vertical = 4.dp)) if(!(VERSION.SDK_INT >=31 && context.isProfileOwner && selectedPermission.profileOwnerRestricted)) { - GrantPermissionItem(R.string.grant, PERMISSION_GRANT_STATE_GRANTED) + GrantPermissionItem(R.string.granted, PERMISSION_GRANT_STATE_GRANTED) } - GrantPermissionItem(R.string.deny, PERMISSION_GRANT_STATE_DENIED) + GrantPermissionItem(R.string.denied, PERMISSION_GRANT_STATE_DENIED) GrantPermissionItem(R.string.default_stringres, PERMISSION_GRANT_STATE_DEFAULT) } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt index 84e5dfc..7dd8afa 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt @@ -16,6 +16,7 @@ import android.content.pm.IPackageInstaller import android.content.pm.PackageInstaller import android.content.pm.PackageManager import android.os.Build.VERSION +import android.os.UserManager import androidx.activity.result.ActivityResultLauncher import androidx.annotation.DrawableRes import androidx.annotation.RequiresApi @@ -401,3 +402,24 @@ data class SecurityEventItem( @SerialName("log_level") val logLevel: Int?, val data: String ) + +fun setDefaultAffiliationID(context: Context) { + if(VERSION.SDK_INT < 26) return + val sharedPrefs = context.getSharedPreferences("data", Context.MODE_PRIVATE) + if(!sharedPrefs.getBoolean("default_affiliation_id_set", false)) { + try { + val um = context.getSystemService(Context.USER_SERVICE) as UserManager + if(context.isDeviceOwner || (!um.isSystemUser && context.isProfileOwner)) { + val dpm = context.getDPM() + val receiver = context.getReceiver() + val affiliationIDs = dpm.getAffiliationIds(receiver) + if(affiliationIDs.isEmpty()) { + dpm.setAffiliationIds(receiver, setOf("OwnDroid_default_affiliation_id")) + sharedPrefs.edit().putBoolean("default_affiliation_id_set", true).apply() + } + } + } catch(e: Exception) { + e.printStackTrace() + } + } +} diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt index df130bb..44995ba 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt @@ -700,7 +700,7 @@ private fun WifiAuthKeypair() { Spacer(Modifier.padding(vertical = 5.dp)) OutlinedTextField( value = keyPair, - label = { Text(stringResource(R.string.keypair)) }, + label = { Text(stringResource(R.string.alias)) }, onValueChange = { keyPair = it }, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), @@ -723,7 +723,7 @@ private fun WifiAuthKeypair() { }, modifier = Modifier.fillMaxWidth(0.49F) ) { - Text(stringResource(R.string.add)) + Text(stringResource(R.string.grant)) } Button( onClick = { @@ -732,7 +732,7 @@ private fun WifiAuthKeypair() { }, modifier = Modifier.fillMaxWidth(0.96F) ) { - Text(stringResource(R.string.remove)) + Text(stringResource(R.string.revoke)) } } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt index e544e3e..d47ed2e 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt @@ -7,8 +7,10 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager +import android.os.Binder import android.os.Build.VERSION import android.os.RemoteException +import android.os.UserManager import android.widget.Toast import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.* @@ -90,6 +92,7 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) { val deviceAdmin = context.isDeviceAdmin val deviceOwner = context.isDeviceOwner val profileOwner = context.isProfileOwner + val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager var dialog by remember { mutableIntStateOf(0) } val enrollmentSpecificId = if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) dpm.enrollmentSpecificId else "" Column(modifier = Modifier.fillMaxSize().verticalScroll(listScrollState)) { @@ -110,13 +113,13 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) { R.string.device_admin, stringResource(if(deviceAdmin) R.string.activated else R.string.deactivated), operation = { localNavCtrl.navigate("DeviceAdmin") } ) - if(profileOwner) { + if(profileOwner || !userManager.isSystemUser) { SubPageItem( - R.string.profile_owner, stringResource(R.string.activated), + R.string.profile_owner, stringResource(if(profileOwner) R.string.activated else R.string.deactivated), operation = { localNavCtrl.navigate("ProfileOwner") } ) } - if(!profileOwner) { + if(!profileOwner && userManager.isSystemUser) { SubPageItem( R.string.device_owner, stringResource(if(deviceOwner) R.string.activated else R.string.deactivated), operation = { localNavCtrl.navigate("DeviceOwner") } @@ -376,7 +379,13 @@ private fun ProfileOwner() { Text(stringResource(R.string.deactivate)) } } - InfoCard(R.string.profile_owner) + if(!profileOwner) { + val command = context.getString(R.string.activate_profile_owner_command, (Binder.getCallingUid() / 100000).toString()) + SelectionContainer { + Text(command) + } + CopyTextButton(R.string.copy_command, command) + } } if(deactivateDialog && VERSION.SDK_INT >= 24) { AlertDialog( @@ -394,7 +403,8 @@ private fun ProfileOwner() { onClick = { dpm.clearProfileOwner(receiver) deactivateDialog = false - } + }, + colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error) ) { Text(stringResource(R.string.confirm)) } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt index 2b69a10..6fb1803 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.selection.SelectionContainer @@ -192,9 +193,7 @@ fun ShizukuActivate() { } } - SelectionContainer(modifier = Modifier - .align(Alignment.Start) - .horizontalScroll(outputTextScrollState)) { + SelectionContainer(modifier = Modifier.fillMaxWidth().horizontalScroll(outputTextScrollState)) { Text(text = outputText, softWrap = false, modifier = Modifier.padding(4.dp)) } 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 9f221bf..fe6fbde 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt @@ -125,6 +125,7 @@ import com.bintianqi.owndroid.ui.SubPageItem import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.TopBar import com.bintianqi.owndroid.uriToStream +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json @@ -1255,7 +1256,6 @@ private fun WipeData() { val context = LocalContext.current val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager val dpm = context.getDPM() - val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current var warning by remember { mutableStateOf(false) } var wipeDevice by remember { mutableStateOf(false) } @@ -1333,6 +1333,14 @@ private fun WipeData() { }, onDismissRequest = { warning = false }, confirmButton = { + var timer by remember { mutableIntStateOf(6) } + LaunchedEffect(Unit) { + while(timer > 0) { + timer -= 1 + delay(1000) + } + } + val timerText = if(timer > 0) "(${timer}s)" else "" TextButton( onClick = { var flag = 0 @@ -1350,9 +1358,11 @@ private fun WipeData() { } } }, - colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error) + colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error), + modifier = Modifier.animateContentSize(), + enabled = timer == 0 ) { - Text(stringResource(R.string.confirm)) + Text(stringResource(R.string.confirm) + timerText) } }, dismissButton = { @@ -1392,27 +1402,30 @@ private fun SysUpdatePolicy() { RadioButtonItem(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)) - 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.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.fillMaxWidth() - ) - Spacer(Modifier.padding(vertical = 3.dp)) - Text(text = stringResource(R.string.minutes_in_one_day)) + AnimatedVisibility(selectedPolicy == 2) { + Column { + Row( + horizontalArrangement = Arrangement.SpaceBetween + ) { + 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.fillMaxWidth(0.49F) + ) + 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.fillMaxWidth(0.96F).padding(bottom = 2.dp) + ) + } + Text(text = stringResource(R.string.minutes_in_one_day)) + } } Button( onClick = { @@ -1426,7 +1439,7 @@ private fun SysUpdatePolicy() { dpm.setSystemUpdatePolicy(receiver,policy) Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() }, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth().padding(top = 8.dp) ) { Text(stringResource(R.string.apply)) } diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index b3ac381..09afc35 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -22,6 +22,7 @@ Запрещено Разрешить Запретить + Revoke Текущее состояние: %1$s Авто Пароль @@ -59,6 +60,7 @@ Следующий On Off + Alias diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 462c1d3..ec616f2 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -23,6 +23,7 @@ Reddedildi Ver Reddet + Revoke Mevcut Durum: %1$s Otomatik Şifre @@ -60,6 +61,7 @@ Next On Off + Alias Etkinleştirmek İçin Tıklayın diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index de3a63a..dc26823 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -20,8 +20,9 @@ 白名单 允许 拒绝 - 允许 + 授予 拒绝 + 吊销 当前状态:%1$s 自动 密码 @@ -57,6 +58,7 @@ 下一个 开启 关闭 + 别名 点击以激活 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6c45c46..3e74045 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -23,6 +23,7 @@ Denied Grant Deny + Revoke Current status: %1$s Auto Password @@ -60,6 +61,7 @@ Next On Off + Alias Click to activate @@ -71,6 +73,7 @@ Reset device policy Activate Device admin dpm set-active-admin com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver + dpm set-profile-owner --user %1$s com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver dpm set-device-owner com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver Device info Support Device ID attestation