Set default Affiliation ID while app launch

Wipe data warning timer
Display profile owner entry in non-system users
Fix some UI bugs
This commit is contained in:
BinTianqi
2024-11-17 10:40:01 +08:00
parent 017ed57f64
commit 2c1898e4a0
12 changed files with 112 additions and 47 deletions

View File

@@ -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<MyViewModel>()
if(!vm.initialized) vm.initialize(applicationContext)
if(!vm.initialized) vm.initialize(context)
lifecycleScope.launch { setDefaultAffiliationID(context) }
setContent {
OwnDroidTheme(vm) {
Home(vm)

View File

@@ -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))
}
}
)

View File

@@ -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)
}
}

View File

@@ -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()
}
}
}

View File

@@ -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))
}
}
}

View File

@@ -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))
}

View File

@@ -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))
}

View File

@@ -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,28 +1402,31 @@ 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))
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.5F)
modifier = Modifier.fillMaxWidth(0.49F)
)
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()
modifier = Modifier.fillMaxWidth(0.96F).padding(bottom = 2.dp)
)
Spacer(Modifier.padding(vertical = 3.dp))
}
Text(text = stringResource(R.string.minutes_in_one_day))
}
}
Button(
onClick = {
val policy =
@@ -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))
}

View File

@@ -22,6 +22,7 @@
<string name="denied">Запрещено</string>
<string name="grant">Разрешить</string>
<string name="deny">Запретить</string>
<string name="revoke">Revoke</string> <!--TODO-->
<string name="current_state">Текущее состояние: %1$s</string>
<string name="auto">Авто</string>
<string name="password">Пароль</string>
@@ -59,6 +60,7 @@
<string name="next">Следующий</string>
<string name="on">On</string> <!--TODO-->
<string name="off">Off</string> <!--TODO-->
<string name="alias">Alias</string> <!--TODO-->
<!--Разрешения-->

View File

@@ -23,6 +23,7 @@
<string name="denied">Reddedildi</string>
<string name="grant">Ver</string>
<string name="deny">Reddet</string>
<string name="revoke">Revoke</string> <!--TODO-->
<string name="current_state">Mevcut Durum: %1$s</string>
<string name="auto">Otomatik</string>
<string name="password">Şifre</string>
@@ -60,6 +61,7 @@
<string name="next">Next</string> <!--TODO-->
<string name="on">On</string> <!--TODO-->
<string name="off">Off</string> <!--TODO-->
<string name="alias">Alias</string> <!--TODO-->
<!--Permissions-->
<string name="click_to_activate">Etkinleştirmek İçin Tıklayın</string>

View File

@@ -20,8 +20,9 @@
<string name="whitelist">白名单</string>
<string name="granted">允许</string>
<string name="denied">拒绝</string>
<string name="grant">允许</string>
<string name="grant">授予</string>
<string name="deny">拒绝</string>
<string name="revoke">吊销</string>
<string name="current_state">当前状态:%1$s</string>
<string name="auto">自动</string>
<string name="password">密码</string>
@@ -57,6 +58,7 @@
<string name="next">下一个</string>
<string name="on">开启</string>
<string name="off">关闭</string>
<string name="alias">别名</string>
<!--Permissions-->
<string name="click_to_activate">点击以激活</string>

View File

@@ -23,6 +23,7 @@
<string name="denied">Denied</string>
<string name="grant">Grant</string>
<string name="deny">Deny</string>
<string name="revoke">Revoke</string>
<string name="current_state">Current status: %1$s</string>
<string name="auto">Auto</string>
<string name="password">Password</string>
@@ -60,6 +61,7 @@
<string name="next">Next</string>
<string name="on">On</string>
<string name="off">Off</string>
<string name="alias">Alias</string>
<!--Permissions-->
<string name="click_to_activate">Click to activate</string>
@@ -71,6 +73,7 @@
<string name="reset_device_policy">Reset device policy</string>
<string name="activate_device_admin">Activate Device admin</string>
<string name="activate_device_admin_command" translatable="false">dpm set-active-admin com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver</string>
<string name="activate_profile_owner_command" translatable="false">dpm set-profile-owner --user %1$s com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver</string>
<string name="activate_device_owner_command" translatable="false">dpm set-device-owner com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver</string>
<string name="device_info">Device info</string>
<string name="support_device_id_attestation">Support Device ID attestation</string>