ViewModel refactoring: Permissions part

Add MyDbHelper and MyRepository, use database to store dhizuku clients,
fix #168
This commit is contained in:
BinTianqi
2025-09-23 20:41:22 +08:00
parent 26c956a2cf
commit 2c72912ea6
11 changed files with 588 additions and 480 deletions

View File

@@ -7,7 +7,6 @@ import androidx.activity.viewModels
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.ui.AppInstaller import com.bintianqi.owndroid.ui.AppInstaller
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
@@ -15,11 +14,10 @@ class AppInstallerActivity:FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge() enableEdgeToEdge()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val myVm by viewModels<MyViewModel>()
val vm by viewModels<AppInstallerViewModel>() val vm by viewModels<AppInstallerViewModel>()
vm.initialize(intent) vm.initialize(intent)
val theme = ThemeSettings(SP.materialYou, SP.darkTheme, SP.blackTheme)
setContent { setContent {
val theme by myVm.theme.collectAsStateWithLifecycle()
OwnDroidTheme(theme) { OwnDroidTheme(theme) {
val uiState by vm.uiState.collectAsState() val uiState by vm.uiState.collectAsState()
AppInstaller( AppInstaller(

View File

@@ -9,7 +9,6 @@ import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
@@ -24,7 +23,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
import com.google.accompanist.drawablepainter.rememberDrawablePainter import com.google.accompanist.drawablepainter.rememberDrawablePainter
import com.rosan.dhizuku.aidl.IDhizukuClient import com.rosan.dhizuku.aidl.IDhizukuClient
@@ -34,12 +32,9 @@ import com.rosan.dhizuku.server_api.DhizukuService
import com.rosan.dhizuku.shared.DhizukuVariables import com.rosan.dhizuku.shared.DhizukuVariables
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
private const val TAG = "DhizukuServer" private const val TAG = "DhizukuServer"
const val DHIZUKU_CLIENTS_FILE = "dhizuku_clients.json"
class MyDhizukuProvider(): DhizukuProvider() { class MyDhizukuProvider(): DhizukuProvider() {
override fun onCreateService(client: IDhizukuClient): DhizukuService? { override fun onCreateService(client: IDhizukuClient): DhizukuService? {
Log.d(TAG, "Creating MyDhizukuService") Log.d(TAG, "Creating MyDhizukuService")
@@ -56,8 +51,6 @@ class MyDhizukuService(context: Context, admin: ComponentName, client: IDhizukuC
pm.getNameForUid(callingUid) ?: return false, pm.getNameForUid(callingUid) ?: return false,
if (Build.VERSION.SDK_INT >= 28) PackageManager.GET_SIGNING_CERTIFICATES else PackageManager.GET_SIGNATURES if (Build.VERSION.SDK_INT >= 28) PackageManager.GET_SIGNING_CERTIFICATES else PackageManager.GET_SIGNATURES
) )
val file = mContext.filesDir.resolve(DHIZUKU_CLIENTS_FILE)
val clients = Json.decodeFromString<List<DhizukuClientInfo>>(file.readText())
val signature = getPackageSignature(packageInfo) val signature = getPackageSignature(packageInfo)
val requiredPermission = when (func) { val requiredPermission = when (func) {
"remote_transact", "remote_process" -> func "remote_transact", "remote_process" -> func
@@ -65,9 +58,10 @@ class MyDhizukuService(context: Context, admin: ComponentName, client: IDhizukuC
"get_delegated_scopes", "set_delegated_scopes" -> "delegated_scopes" "get_delegated_scopes", "set_delegated_scopes" -> "delegated_scopes"
else -> "other" else -> "other"
} }
val hasPermission = clients.find { val hasPermission = (mContext.applicationContext as MyApplication).myRepo
callingUid == it.uid && signature == it.signature && requiredPermission in it.permissions .checkDhizukuClientPermission(
} != null callingUid, signature, requiredPermission
)
Log.d(TAG, "UID $callingUid, PID $callingPid, required permission: $requiredPermission, has permission: $hasPermission") Log.d(TAG, "UID $callingUid, PID $callingPid, required permission: $requiredPermission, has permission: $hasPermission")
return hasPermission return hasPermission
} }
@@ -97,26 +91,19 @@ class DhizukuActivity : ComponentActivity() {
val icon = appInfo.loadIcon(packageManager) val icon = appInfo.loadIcon(packageManager)
val label = appInfo.loadLabel(packageManager).toString() val label = appInfo.loadLabel(packageManager).toString()
fun close(grantPermission: Boolean) { fun close(grantPermission: Boolean) {
val file = filesDir.resolve(DHIZUKU_CLIENTS_FILE)
val json = Json { ignoreUnknownKeys = true }
val clients = json.decodeFromString<MutableList<DhizukuClientInfo>>(file.readText())
val index = clients.indexOfFirst { it.uid == uid }
val clientInfo = DhizukuClientInfo( val clientInfo = DhizukuClientInfo(
uid, getPackageSignature(packageInfo), if (grantPermission) DhizukuPermissions else emptyList() uid, getPackageSignature(packageInfo), if (grantPermission) DhizukuPermissions else emptyList()
) )
if (index == -1) clients += clientInfo (application as MyApplication).myRepo.setDhizukuClient(clientInfo)
else clients[index] = clientInfo
file.writeText(Json.encodeToString(clients))
finish() finish()
listener.onRequestPermission( listener.onRequestPermission(
if (grantPermission) PackageManager.PERMISSION_GRANTED else PackageManager.PERMISSION_DENIED if (grantPermission) PackageManager.PERMISSION_GRANTED else PackageManager.PERMISSION_DENIED
) )
} }
val vm by viewModels<MyViewModel>()
enableEdgeToEdge() enableEdgeToEdge()
val theme = ThemeSettings(SP.materialYou, SP.darkTheme, SP.blackTheme)
setContent { setContent {
var appLockDialog by remember { mutableStateOf(false) } var appLockDialog by remember { mutableStateOf(false) }
val theme by vm.theme.collectAsStateWithLifecycle()
OwnDroidTheme(theme) { OwnDroidTheme(theme) {
if (!appLockDialog) AlertDialog( if (!appLockDialog) AlertDialog(
icon = { icon = {

View File

@@ -293,23 +293,40 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
) { ) {
composable<Home> { HomeScreen(::navigate) } composable<Home> { HomeScreen(::navigate) }
composable<WorkModes> { composable<WorkModes> {
WorkModesScreen(it.toRoute(), ::navigateUp, { WorkModesScreen(vm, it.toRoute(), ::navigateUp, {
navController.navigate(Home) { navController.navigate(Home) {
popUpTo<WorkModes> { inclusive = true } popUpTo<WorkModes> { inclusive = true }
} }
}, {
navController.navigate(WorkModes(false)) {
popUpTo(Home) { inclusive = true }
}
}, ::navigate) }, ::navigate)
} }
composable<DhizukuServerSettings> { DhizukuServerSettingsScreen(::navigateUp) } composable<DhizukuServerSettings> {
DhizukuServerSettingsScreen(vm.dhizukuClients, vm::getDhizukuClients,
composable<DelegatedAdmins> { DelegatedAdminsScreen(::navigateUp, ::navigate) } vm::updateDhizukuClient, vm::getDhizukuServerEnabled, vm::setDhizukuServerEnabled,
composable<AddDelegatedAdmin>{ ::navigateUp)
AddDelegatedAdminScreen(vm.chosenPackage, ::choosePackage, it.toRoute(), ::navigateUp) }
composable<DelegatedAdmins> {
DelegatedAdminsScreen(vm.delegatedAdmins, vm::getDelegatedAdmins, ::navigateUp, ::navigate)
}
composable<AddDelegatedAdmin>{
AddDelegatedAdminScreen(vm.chosenPackage, ::choosePackage, it.toRoute(),
vm::setDelegatedAdmin, ::navigateUp)
}
composable<DeviceInfo> { DeviceInfoScreen(vm, ::navigateUp) }
composable<LockScreenInfo> {
LockScreenInfoScreen(vm::getLockScreenInfo, vm::setLockScreenInfo, ::navigateUp)
}
composable<SupportMessage> {
SupportMessageScreen(vm::getShortSupportMessage, vm::getLongSupportMessage,
vm::setShortSupportMessage, vm::setLongSupportMessage, ::navigateUp)
} }
composable<DeviceInfo> { DeviceInfoScreen(::navigateUp) }
composable<LockScreenInfo> { LockScreenInfoScreen(::navigateUp) }
composable<SupportMessage> { SupportMessageScreen(::navigateUp) }
composable<TransferOwnership> { composable<TransferOwnership> {
TransferOwnershipScreen(::navigateUp) { TransferOwnershipScreen(vm.deviceAdminReceivers, vm::getDeviceAdminReceivers,
vm::transferOwnership, ::navigateUp) {
navController.navigate(WorkModes(false)) { navController.navigate(WorkModes(false)) {
popUpTo(Home) { inclusive = true } popUpTo(Home) { inclusive = true }
} }

View File

@@ -5,12 +5,14 @@ import android.os.Build.VERSION
import org.lsposed.hiddenapibypass.HiddenApiBypass import org.lsposed.hiddenapibypass.HiddenApiBypass
class MyApplication : Application() { class MyApplication : Application() {
lateinit var myRepo: MyRepository
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
if (VERSION.SDK_INT >= 28) HiddenApiBypass.setHiddenApiExemptions("") if (VERSION.SDK_INT >= 28) HiddenApiBypass.setHiddenApiExemptions("")
SP = SharedPrefs(applicationContext) SP = SharedPrefs(applicationContext)
val dbHelper = MyDbHelper(this)
myRepo = MyRepository(dbHelper)
Privilege.initialize(applicationContext) Privilege.initialize(applicationContext)
Privilege.updateStatus()
} }
} }

View File

@@ -0,0 +1,15 @@
package com.bintianqi.owndroid
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
class MyDbHelper(context: Context): SQLiteOpenHelper(context, "data", null, 1) {
override fun onCreate(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE dhizuku_clients (uid INTEGER PRIMARY KEY," +
"signature TEXT, permissions TEXT)")
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
}
}

View File

@@ -0,0 +1,46 @@
package com.bintianqi.owndroid
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
class MyRepository(val dbHelper: MyDbHelper) {
fun getDhizukuClients(): List<DhizukuClientInfo> {
val list = mutableListOf<DhizukuClientInfo>()
dbHelper.readableDatabase.rawQuery("SELECT * FROM dhizuku_clients", null).use { cursor ->
while (cursor.moveToNext()) {
list += DhizukuClientInfo(
cursor.getInt(0), cursor.getString(1),
cursor.getString(2).split(",").filter { it.isNotEmpty() }
)
}
}
return list
}
fun checkDhizukuClientPermission(uid: Int, signature: String?, permission: String): Boolean {
val cursor = if (signature == null) {
dbHelper.readableDatabase.rawQuery(
"SELECT permissions FROM dhizuku_clients WHERE uid = $uid AND signature IS NULL",
null
)
} else {
dbHelper.readableDatabase.rawQuery(
"SELECT permissions FROM dhizuku_clients WHERE uid = $uid AND signature = ?",
arrayOf(signature)
)
}
return cursor.use {
it.moveToNext() && permission in it.getString(0).split(",")
}
}
fun setDhizukuClient(info: DhizukuClientInfo) {
val cv = ContentValues()
cv.put("uid", info.uid)
cv.put("signature", info.signature)
cv.put("permissions", info.permissions.joinToString(","))
dbHelper.writableDatabase.insertWithOnConflict("dhizuku_clients", null, cv,
SQLiteDatabase.CONFLICT_REPLACE)
}
fun deleteDhizukuClient(info: DhizukuClientInfo) {
dbHelper.writableDatabase.delete("dhizuku_clients", "uid = ${info.uid}", null)
}
}

View File

@@ -3,6 +3,8 @@ package com.bintianqi.owndroid
import android.app.ActivityOptions import android.app.ActivityOptions
import android.app.Application import android.app.Application
import android.app.PendingIntent import android.app.PendingIntent
import android.app.admin.DeviceAdminInfo
import android.app.admin.DeviceAdminReceiver
import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback
import android.app.admin.FactoryResetProtectionPolicy import android.app.admin.FactoryResetProtectionPolicy
@@ -30,18 +32,25 @@ import androidx.lifecycle.application
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.bintianqi.owndroid.Privilege.DAR import com.bintianqi.owndroid.Privilege.DAR
import com.bintianqi.owndroid.Privilege.DPM import com.bintianqi.owndroid.Privilege.DPM
import com.bintianqi.owndroid.dpm.ACTIVATE_DEVICE_OWNER_COMMAND
import com.bintianqi.owndroid.dpm.AppStatus import com.bintianqi.owndroid.dpm.AppStatus
import com.bintianqi.owndroid.dpm.CaCertInfo import com.bintianqi.owndroid.dpm.CaCertInfo
import com.bintianqi.owndroid.dpm.DelegatedAdmin
import com.bintianqi.owndroid.dpm.DeviceAdmin
import com.bintianqi.owndroid.dpm.FrpPolicyInfo import com.bintianqi.owndroid.dpm.FrpPolicyInfo
import com.bintianqi.owndroid.dpm.HardwareProperties import com.bintianqi.owndroid.dpm.HardwareProperties
import com.bintianqi.owndroid.dpm.PendingSystemUpdateInfo import com.bintianqi.owndroid.dpm.PendingSystemUpdateInfo
import com.bintianqi.owndroid.dpm.SystemOptionsStatus import com.bintianqi.owndroid.dpm.SystemOptionsStatus
import com.bintianqi.owndroid.dpm.SystemUpdatePolicyInfo import com.bintianqi.owndroid.dpm.SystemUpdatePolicyInfo
import com.bintianqi.owndroid.dpm.delegatedScopesList
import com.bintianqi.owndroid.dpm.getPackageInstaller import com.bintianqi.owndroid.dpm.getPackageInstaller
import com.bintianqi.owndroid.dpm.isValidPackageName import com.bintianqi.owndroid.dpm.isValidPackageName
import com.bintianqi.owndroid.dpm.parsePackageInstallerMessage import com.bintianqi.owndroid.dpm.parsePackageInstallerMessage
import com.bintianqi.owndroid.dpm.permissionList import com.bintianqi.owndroid.dpm.permissionList
import com.bintianqi.owndroid.dpm.temperatureTypes import com.bintianqi.owndroid.dpm.temperatureTypes
import com.rosan.dhizuku.api.Dhizuku
import com.rosan.dhizuku.api.DhizukuRequestPermissionListener
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
@@ -56,6 +65,7 @@ import java.security.cert.X509Certificate
import java.util.concurrent.Executors import java.util.concurrent.Executors
class MyViewModel(application: Application): AndroidViewModel(application) { class MyViewModel(application: Application): AndroidViewModel(application) {
val myRepo = getApplication<MyApplication>().myRepo
val PM = application.packageManager val PM = application.packageManager
val theme = MutableStateFlow(ThemeSettings(SP.materialYou, SP.darkTheme, SP.blackTheme)) val theme = MutableStateFlow(ThemeSettings(SP.materialYou, SP.darkTheme, SP.blackTheme))
fun changeTheme(newTheme: ThemeSettings) { fun changeTheme(newTheme: ThemeSettings) {
@@ -787,6 +797,205 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
} }
DPM.installSystemUpdate(DAR, uri, application.mainExecutor, callback) DPM.installSystemUpdate(DAR, uri, application.mainExecutor, callback)
} }
@RequiresApi(24)
fun isCreatingWorkProfileAllowed(): Boolean {
return DPM.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)
}
fun activateDoByShizuku(callback: (Boolean, String?) -> Unit) {
viewModelScope.launch {
useShizuku(application) { service ->
try {
val result = IUserService.Stub.asInterface(service)
.execute(ACTIVATE_DEVICE_OWNER_COMMAND)
if (result == null || result.getInt("code", -1) != 0) {
callback(false, null)
} else {
Privilege.updateStatus()
callback(
true, result.getString("output") + "\n" + result.getString("error")
)
}
} catch (e: Exception) {
e.printStackTrace()
callback(false, null)
}
}
}
}
fun activateDoByRoot(callback: (Boolean, String?) -> Unit) {
Shell.getShell { shell ->
if(shell.isRoot) {
val result = Shell.cmd(ACTIVATE_DEVICE_OWNER_COMMAND).exec()
val output = result.out.joinToString("\n") + "\n" + result.err.joinToString("\n")
Privilege.updateStatus()
callback(result.isSuccess, output)
} else {
callback(false, application.getString(R.string.permission_denied))
}
}
}
@RequiresApi(28)
fun activateDoByDhizuku(callback: (Boolean, String?) -> Unit) {
DPM.transferOwnership(DAR, MyAdminComponent, null)
SP.dhizuku = false
Privilege.initialize(application)
callback(true, null)
}
fun activateDhizukuMode(callback: (Boolean, String?) -> Unit) {
fun onSucceed() {
SP.dhizuku = true
Privilege.initialize(application)
callback(true, null)
}
if (Dhizuku.init(application)) {
if (Dhizuku.isPermissionGranted()) {
onSucceed()
} else {
Dhizuku.requestPermission(object : DhizukuRequestPermissionListener() {
override fun onRequestPermission(grantResult: Int) {
if(grantResult == PackageManager.PERMISSION_GRANTED) onSucceed()
}
})
}
} else {
callback(false, application.getString(R.string.failed_to_init_dhizuku))
}
}
fun clearDeviceOwner() {
DPM.clearDeviceOwnerApp(application.packageName)
}
@RequiresApi(24)
fun clearProfileOwner() {
DPM.clearProfileOwner(MyAdminComponent)
}
fun deactivateDhizukuMode() {
SP.dhizuku = false
Privilege.initialize(application)
}
val dhizukuClients = MutableStateFlow(emptyList<Pair<DhizukuClientInfo, AppInfo>>())
fun getDhizukuClients() {
viewModelScope.launch {
dhizukuClients.value = myRepo.getDhizukuClients().mapNotNull {
val packageName = PM.getNameForUid(it.uid)
if (packageName == null) {
myRepo.deleteDhizukuClient(it)
null
} else {
it to getAppInfo(packageName)
}
}
}
}
fun getDhizukuServerEnabled(): Boolean {
return SP.dhizukuServer
}
fun setDhizukuServerEnabled(status: Boolean) {
SP.dhizukuServer = status
}
fun updateDhizukuClient(info: DhizukuClientInfo) {
myRepo.setDhizukuClient(info)
dhizukuClients.update { list ->
val ml = list.toMutableList()
val index = ml.indexOfFirst { it.first.uid == info.uid }
ml[index] = info to ml[index].second
ml
}
}
@RequiresApi(24)
fun getLockScreenInfo(): String {
return DPM.deviceOwnerLockScreenInfo?.toString() ?: ""
}
@RequiresApi(24)
fun setLockScreenInfo(text: String) {
DPM.setDeviceOwnerLockScreenInfo(DAR, text)
}
val delegatedAdmins = MutableStateFlow(emptyList<DelegatedAdmin>())
@RequiresApi(26)
fun getDelegatedAdmins() {
val list = mutableListOf<DelegatedAdmin>()
delegatedScopesList.forEach { scope ->
DPM.getDelegatePackages(DAR, scope.id)?.forEach { pkg ->
val index = list.indexOfFirst { it.app.name == pkg }
if (index == -1) {
list += DelegatedAdmin(getAppInfo(pkg), listOf(scope.id))
} else {
list[index] = DelegatedAdmin(list[index].app, list[index].scopes + scope.id)
}
}
}
delegatedAdmins.value = list
}
@RequiresApi(26)
fun setDelegatedAdmin(name: String, scopes: List<String>) {
DPM.setDelegatedScopes(DAR, name, scopes)
getDelegatedAdmins()
}
@RequiresApi(34)
fun getDeviceFinanced(): Boolean {
return DPM.isDeviceFinanced
}
@RequiresApi(33)
fun getDpmRh(): String? {
return DPM.devicePolicyManagementRoleHolderPackage
}
fun getStorageEncryptionStatus(): Int {
return DPM.storageEncryptionStatus
}
@RequiresApi(28)
fun getDeviceIdAttestationSupported(): Boolean {
return DPM.isDeviceIdAttestationSupported
}
@RequiresApi(30)
fun getUniqueDeviceAttestationSupported(): Boolean {
return DPM.isUniqueDeviceAttestationSupported
}
fun getActiveAdmins(): String {
return DPM.activeAdmins?.joinToString("\n") {
it.flattenToShortString()
} ?: application.getString(R.string.none)
}
@RequiresApi(24)
fun getShortSupportMessage(): String {
return DPM.getShortSupportMessage(DAR)?.toString() ?: ""
}
@RequiresApi(24)
fun getLongSupportMessage(): String {
return DPM.getLongSupportMessage(DAR)?.toString() ?: ""
}
@RequiresApi(24)
fun setShortSupportMessage(text: String?) {
DPM.setShortSupportMessage(DAR, text)
}
@RequiresApi(24)
fun setLongSupportMessage(text: String?) {
DPM.setLongSupportMessage(DAR, text)
}
val deviceAdminReceivers = MutableStateFlow(emptyList<DeviceAdmin>())
fun getDeviceAdminReceivers() {
viewModelScope.launch {
deviceAdminReceivers.value = PM.queryBroadcastReceivers(
Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
PackageManager.GET_META_DATA
).mapNotNull {
try {
DeviceAdminInfo(application, it)
} catch(_: Exception) {
null
}
}.filter {
it.isVisible && it.packageName != "com.bintianqi.owndroid" &&
it.activityInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM == 0
}.map {
DeviceAdmin(getAppInfo(it.packageName), it.component)
}
}
}
@RequiresApi(28)
fun transferOwnership(component: ComponentName) {
DPM.transferOwnership(DAR, component, null)
Privilege.updateStatus()
}
} }
data class ThemeSettings( data class ThemeSettings(

View File

@@ -31,6 +31,7 @@ object Privilege {
} }
DPM = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager DPM = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
DAR = MyAdminComponent DAR = MyAdminComponent
updateStatus()
} }
lateinit var DPM: DevicePolicyManager lateinit var DPM: DevicePolicyManager
private set private set

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,6 @@ import android.app.admin.DevicePolicyManager.WIPE_EUICC
import android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE import android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE
import android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA import android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA
import android.app.admin.DevicePolicyManager.WIPE_SILENTLY import android.app.admin.DevicePolicyManager.WIPE_SILENTLY
import android.app.admin.SystemUpdateInfo
import android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC import android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC
import android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_WINDOWED import android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_WINDOWED
import android.app.admin.SystemUpdatePolicy.TYPE_POSTPONE import android.app.admin.SystemUpdatePolicy.TYPE_POSTPONE
@@ -135,7 +134,6 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.util.Date
import java.util.TimeZone import java.util.TimeZone
import kotlin.math.roundToLong import kotlin.math.roundToLong

View File

@@ -259,7 +259,7 @@ fun InfoItem(title: Int, text: String, withInfo: Boolean = false, onClick: () ->
Modifier.fillMaxWidth().padding(vertical = 6.dp).padding(start = HorizontalPadding, end = 8.dp), Modifier.fillMaxWidth().padding(vertical = 6.dp).padding(start = HorizontalPadding, end = 8.dp),
Arrangement.SpaceBetween, Alignment.CenterVertically Arrangement.SpaceBetween, Alignment.CenterVertically
) { ) {
Column { Column(Modifier.weight(1F)) {
Text(stringResource(title), style = typography.titleLarge) Text(stringResource(title), style = typography.titleLarge)
Text(text, Modifier.alpha(0.8F)) Text(text, Modifier.alpha(0.8F))
} }