Integrate Dhizuku server

This commit is contained in:
BinTianqi
2025-05-24 19:36:43 +08:00
parent b547c8add8
commit ef800fd6bd
14 changed files with 299 additions and 12 deletions

View File

@@ -92,6 +92,7 @@ dependencies {
implementation(libs.shizuku.provider) implementation(libs.shizuku.provider)
implementation(libs.shizuku.api) implementation(libs.shizuku.api)
implementation(libs.dhizuku.api) implementation(libs.dhizuku.api)
implementation(libs.dhizuku.server.api)
implementation(libs.androidx.fragment) implementation(libs.androidx.fragment)
implementation(libs.hiddenApiBypass) implementation(libs.hiddenApiBypass)
implementation(libs.libsu) implementation(libs.libsu)

View File

@@ -20,3 +20,7 @@
# If you keep the line number information, uncomment this to # If you keep the line number information, uncomment this to
# hide the original source file name. # hide the original source file name.
# -renamesourcefileattribute SourceFile # -renamesourcefileattribute SourceFile
-dontwarn android.app.ActivityThread
-dontwarn android.app.ContextImpl
-dontwarn android.app.LoadedApk

View File

@@ -68,6 +68,17 @@
android:permission="com.bintianqi.owndroid.MyPermission" android:permission="com.bintianqi.owndroid.MyPermission"
android:exported="true" android:exported="true"
android:theme="@android:style/Theme.NoDisplay" /> android:theme="@android:style/Theme.NoDisplay" />
<activity
android:name=".DhizukuActivity"
android:excludeFromRecents="true"
android:exported="true"
android:launchMode="singleInstance"
android:theme="@style/Theme.Transparent">
<intent-filter>
<action android:name="com.bintianqi.owndroid.action.REQUEST_DHIZUKU_PERMISSION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<receiver <receiver
android:name=".Receiver" android:name=".Receiver"
android:description="@string/app_name" android:description="@string/app_name"
@@ -97,5 +108,12 @@
android:enabled="true" android:enabled="true"
android:exported="true" android:exported="true"
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" /> android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
<provider
android:name=".MyDhizukuProvider"
android:authorities="com.bintianqi.owndroid.dhizuku_server.provider"
android:directBootAware="true"
android:enabled="true"
android:exported="true">
</provider>
</application> </application>
</manifest> </manifest>

View File

@@ -0,0 +1,147 @@
package com.bintianqi.owndroid
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.size
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import com.rosan.dhizuku.aidl.IDhizukuClient
import com.rosan.dhizuku.aidl.IDhizukuRequestPermissionListener
import com.rosan.dhizuku.server_api.DhizukuProvider
import com.rosan.dhizuku.server_api.DhizukuService
import com.rosan.dhizuku.shared.DhizukuVariables
import kotlinx.coroutines.delay
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
private const val TAG = "DhizukuServer"
const val DHIZUKU_CLIENTS_FILE = "dhizuku_clients.json"
class MyDhizukuProvider(): DhizukuProvider() {
override fun onCreateService(client: IDhizukuClient): DhizukuService? {
Log.d(TAG, "Creating MyDhizukuService")
return if (SharedPrefs(context!!).dhizukuServer) MyDhizukuService(context!!, MyAdminComponent, client) else null
}
}
class MyDhizukuService(context: Context, admin: ComponentName, client: IDhizukuClient) :
DhizukuService(context, admin, client) {
override fun checkCallingPermission(func: String?, callingUid: Int, callingPid: Int): Boolean {
if (!SharedPrefs(mContext).dhizukuServer) return false
val pm = mContext.packageManager
val packageInfo = pm.getPackageInfo(
pm.getNameForUid(callingUid) ?: return false,
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 hasPermission = DhizukuClientInfo(callingUid, signature, true) in clients
Log.d(TAG, "UID $callingUid, PID $callingPid, has permission: $hasPermission")
return hasPermission
}
override fun getVersionName() = "1.0"
}
class DhizukuActivity : ComponentActivity() {
@OptIn(ExperimentalStdlibApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!SharedPrefs(this).dhizukuServer) {
finish()
return
}
val bundle = intent.extras ?: return
val uid = bundle.getInt(DhizukuVariables.PARAM_CLIENT_UID, -1)
if (uid == -1) return
val binder = bundle.getBinder(DhizukuVariables.PARAM_CLIENT_REQUEST_PERMISSION_BINDER) ?: return
val listener = IDhizukuRequestPermissionListener.Stub.asInterface(binder)
val packageName = packageManager.getPackagesForUid(uid)?.first() ?: return
val packageInfo = packageManager.getPackageInfo(
packageName,
if (Build.VERSION.SDK_INT >= 28) PackageManager.GET_SIGNING_CERTIFICATES else PackageManager.GET_SIGNATURES
)
val appInfo = packageManager.getApplicationInfo(packageName, 0)
val icon = appInfo.loadIcon(packageManager)
val label = appInfo.loadLabel(packageManager).toString()
fun close(grantPermission: Boolean) {
val file = filesDir.resolve(DHIZUKU_CLIENTS_FILE)
val clients = Json.decodeFromString<MutableList<DhizukuClientInfo>>(file.readText())
val index = clients.indexOfFirst { it.uid == uid }
val clientInfo = DhizukuClientInfo(uid, getPackageSignature(packageInfo), grantPermission)
if (index == -1) clients += clientInfo
else clients[index] = clientInfo
file.writeText(Json.encodeToString(clients))
finish()
listener.onRequestPermission(
if (grantPermission) PackageManager.PERMISSION_GRANTED else PackageManager.PERMISSION_DENIED
)
}
setContent {
AlertDialog(
icon = {
Image(rememberDrawablePainter(icon), null, Modifier.size(35.dp))
},
title = {
Text(stringResource(R.string.request_permission))
},
text = {
Text("$label\n($packageName)")
},
confirmButton = {
var time by remember { mutableIntStateOf(3) }
LaunchedEffect(Unit) {
(1..3).forEach {
delay(1000)
time -= 1
}
}
TextButton({
close(true)
}, enabled = time == 0) {
val append = if (time > 0) " (${time}s)" else ""
Text(stringResource(R.string.allow) + append)
}
},
dismissButton = {
TextButton({
close(false)
}) {
Text(stringResource(R.string.reject))
}
},
onDismissRequest = {
finish()
}
)
}
}
}
@Serializable
data class DhizukuClientInfo(
val uid: Int,
val signature: String?,
val allow: Boolean
)

View File

@@ -107,6 +107,8 @@ import com.bintianqi.owndroid.dpm.DeleteWorkProfile
import com.bintianqi.owndroid.dpm.DeleteWorkProfileScreen import com.bintianqi.owndroid.dpm.DeleteWorkProfileScreen
import com.bintianqi.owndroid.dpm.DeviceInfo import com.bintianqi.owndroid.dpm.DeviceInfo
import com.bintianqi.owndroid.dpm.DeviceInfoScreen import com.bintianqi.owndroid.dpm.DeviceInfoScreen
import com.bintianqi.owndroid.dpm.DhizukuServerSettings
import com.bintianqi.owndroid.dpm.DhizukuServerSettingsScreen
import com.bintianqi.owndroid.dpm.DisableAccountManagement import com.bintianqi.owndroid.dpm.DisableAccountManagement
import com.bintianqi.owndroid.dpm.DisableAccountManagementScreen import com.bintianqi.owndroid.dpm.DisableAccountManagementScreen
import com.bintianqi.owndroid.dpm.DisableMeteredData import com.bintianqi.owndroid.dpm.DisableMeteredData
@@ -324,6 +326,7 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
} }
}, ::navigate) }, ::navigate)
} }
composable<DhizukuServerSettings> { DhizukuServerSettingsScreen(::navigateUp) }
composable<DelegatedAdmins> { DelegatedAdminsScreen(::navigateUp, ::navigate) } composable<DelegatedAdmins> { DelegatedAdminsScreen(::navigateUp, ::navigate) }
composable<AddDelegatedAdmin>{ AddDelegatedAdminScreen(it.toRoute(), ::navigateUp) } composable<AddDelegatedAdmin>{ AddDelegatedAdminScreen(it.toRoute(), ::navigateUp) }

View File

@@ -64,7 +64,7 @@ class Receiver : DeviceAdminReceiver() {
super.onSecurityLogsAvailable(context, intent) super.onSecurityLogsAvailable(context, intent)
if(VERSION.SDK_INT >= 24) { if(VERSION.SDK_INT >= 24) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val events = getManager(context).retrieveSecurityLogs(ComponentName(context, this@Receiver::class.java)) ?: return@launch val events = getManager(context).retrieveSecurityLogs(MyAdminComponent) ?: return@launch
val file = context.filesDir.resolve("SecurityLogs.json") val file = context.filesDir.resolve("SecurityLogs.json")
val fileExists = file.exists() val fileExists = file.exists()
file.outputStream().use { file.outputStream().use {

View File

@@ -23,7 +23,8 @@ class SharedPrefs(context: Context) {
var biometricsUnlock by BooleanSharedPref("lock.biometrics") var biometricsUnlock by BooleanSharedPref("lock.biometrics")
var lockWhenLeaving by BooleanSharedPref("lock.onleave") var lockWhenLeaving by BooleanSharedPref("lock.onleave")
var applicationsListView by BooleanSharedPref("applications.list_view", true) var applicationsListView by BooleanSharedPref("applications.list_view", true)
var shortcuts by BooleanSharedPref("shortcuts", false) var shortcuts by BooleanSharedPref("shortcuts")
var dhizukuServer by BooleanSharedPref("dhizuku_server")
} }
private class BooleanSharedPref(val key: String, val defValue: Boolean = false): ReadWriteProperty<SharedPrefs, Boolean> { private class BooleanSharedPref(val key: String, val defValue: Boolean = false): ReadWriteProperty<SharedPrefs, Boolean> {

View File

@@ -2,8 +2,10 @@ package com.bintianqi.owndroid
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageInfo
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@@ -131,3 +133,13 @@ fun String.hash(): String {
val md = MessageDigest.getInstance("SHA-256") val md = MessageDigest.getInstance("SHA-256")
return md.digest(this.encodeToByteArray()).toHexString() return md.digest(this.encodeToByteArray()).toHexString()
} }
val MyAdminComponent = ComponentName.unflattenFromString("com.bintianqi.owndroid/.Receiver")!!
@OptIn(ExperimentalStdlibApi::class)
fun getPackageSignature(info: PackageInfo): String? {
val signatures = if (Build.VERSION.SDK_INT >= 28) info.signingInfo?.apkContentsSigners else info.signatures
return signatures?.firstOrNull()?.toByteArray()
?.let { MessageDigest.getInstance("SHA-256").digest(it) }?.toHexString()
}

View File

@@ -24,11 +24,11 @@ import androidx.annotation.RequiresApi
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import com.bintianqi.owndroid.MyAdminComponent
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.Receiver import com.bintianqi.owndroid.Receiver
import com.bintianqi.owndroid.SharedPrefs import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.ShortcutsReceiverActivity import com.bintianqi.owndroid.ShortcutsReceiverActivity
import com.bintianqi.owndroid.backToHomeStateFlow
import com.bintianqi.owndroid.createShortcuts import com.bintianqi.owndroid.createShortcuts
import com.bintianqi.owndroid.myPrivilege import com.bintianqi.owndroid.myPrivilege
import com.rosan.dhizuku.api.Dhizuku import com.rosan.dhizuku.api.Dhizuku
@@ -130,7 +130,7 @@ fun Context.getReceiver(): ComponentName {
return if(SharedPrefs(this).dhizuku) { return if(SharedPrefs(this).dhizuku) {
Dhizuku.getOwnerComponent() Dhizuku.getOwnerComponent()
} else { } else {
ComponentName(this, Receiver::class.java) MyAdminComponent
} }
} }

View File

@@ -19,6 +19,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.annotation.Keep import androidx.annotation.Keep
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -31,7 +32,10 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.text.selection.SelectionContainer
@@ -57,6 +61,7 @@ import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
@@ -84,23 +89,28 @@ import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.ChoosePackageContract import com.bintianqi.owndroid.ChoosePackageContract
import com.bintianqi.owndroid.DHIZUKU_CLIENTS_FILE
import com.bintianqi.owndroid.DhizukuClientInfo
import com.bintianqi.owndroid.HorizontalPadding import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.IUserService import com.bintianqi.owndroid.IUserService
import com.bintianqi.owndroid.MyAdminComponent
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.Receiver
import com.bintianqi.owndroid.Settings import com.bintianqi.owndroid.Settings
import com.bintianqi.owndroid.SharedPrefs import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.myPrivilege import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CircularProgressDialog import com.bintianqi.owndroid.ui.CircularProgressDialog
import com.bintianqi.owndroid.ui.InfoItem import com.bintianqi.owndroid.ui.InfoItem
import com.bintianqi.owndroid.ui.MyLazyScaffold
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.MySmallTitleScaffold import com.bintianqi.owndroid.ui.MySmallTitleScaffold
import com.bintianqi.owndroid.ui.NavIcon import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.Notes import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.updatePrivilege import com.bintianqi.owndroid.updatePrivilege
import com.bintianqi.owndroid.useShizuku import com.bintianqi.owndroid.useShizuku
import com.bintianqi.owndroid.yesOrNo import com.bintianqi.owndroid.yesOrNo
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import com.rosan.dhizuku.api.Dhizuku import com.rosan.dhizuku.api.Dhizuku
import com.rosan.dhizuku.api.DhizukuRequestPermissionListener import com.rosan.dhizuku.api.DhizukuRequestPermissionListener
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
@@ -108,6 +118,8 @@ import com.topjohnwu.superuser.ipc.RootService
import dalvik.system.DexClassLoader import dalvik.system.DexClassLoader
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.lang.invoke.MethodHandles import java.lang.invoke.MethodHandles
import java.lang.reflect.Proxy import java.lang.reflect.Proxy
@@ -258,6 +270,14 @@ fun WorkModesScreen(
tint = if(privilege.device) colorScheme.primary else colorScheme.onBackground tint = if(privilege.device) colorScheme.primary else colorScheme.onBackground
) )
} }
if ((privilege.device || privilege.profile) && !privilege.dhizuku) Row(
Modifier.padding(top = 20.dp).fillMaxWidth().clickable { onNavigate(DhizukuServerSettings) },
verticalAlignment = Alignment.CenterVertically
) {
Icon(painterResource(R.drawable.dhizuku_icon), null, Modifier.padding(8.dp).size(28.dp))
Text(stringResource(R.string.dhizuku_server), style = typography.titleLarge)
}
Column(Modifier.padding(HorizontalPadding, 20.dp)) { Column(Modifier.padding(HorizontalPadding, 20.dp)) {
Row(Modifier.padding(bottom = 4.dp), verticalAlignment = Alignment.CenterVertically) { Row(Modifier.padding(bottom = 4.dp), verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Outlined.Warning, null, Modifier.padding(end = 4.dp), colorScheme.error) Icon(Icons.Outlined.Warning, null, Modifier.padding(end = 4.dp), colorScheme.error)
@@ -338,7 +358,7 @@ fun WorkModesScreen(
if(privilege.device) { if(privilege.device) {
dpm.clearDeviceOwnerApp(context.packageName) dpm.clearDeviceOwnerApp(context.packageName)
} else if(VERSION.SDK_INT >= 24) { } else if(VERSION.SDK_INT >= 24) {
dpm.clearProfileOwner(ComponentName(context, Receiver::class.java)) dpm.clearProfileOwner(MyAdminComponent)
} }
} }
dialog = 0 dialog = 0
@@ -424,10 +444,7 @@ fun activateUsingDhizuku(context: Context, callback: (Boolean, Boolean, String?)
if(dpm == null) { if(dpm == null) {
context.showOperationResultToast(false) context.showOperationResultToast(false)
} else { } else {
dpm.transferOwnership( dpm.transferOwnership(Dhizuku.getOwnerComponent(), MyAdminComponent, PersistableBundle())
Dhizuku.getOwnerComponent(),
ComponentName(context, Receiver::class.java), PersistableBundle()
)
callback(true, true, null) callback(true, true, null)
} }
} catch (e: Exception) { } catch (e: Exception) {
@@ -542,6 +559,65 @@ fun activateDhizukuMode(context: Context, callback: (Boolean, Boolean, String?)
const val ACTIVATE_DEVICE_OWNER_COMMAND = "dpm set-device-owner com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver" const val ACTIVATE_DEVICE_OWNER_COMMAND = "dpm set-device-owner com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver"
@Serializable object DhizukuServerSettings
@Composable
fun DhizukuServerSettingsScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current
val pm = context.packageManager
val sp = SharedPrefs(context)
val file = context.filesDir.resolve(DHIZUKU_CLIENTS_FILE)
var enabled by remember { mutableStateOf(sp.dhizukuServer) }
val clients = remember { mutableStateListOf<DhizukuClientInfo>() }
fun changeEnableState(status: Boolean) {
enabled = status
sp.dhizukuServer = status
}
fun writeList() {
file.writeText(Json.encodeToString(clients))
}
LaunchedEffect(Unit) {
if (!file.exists()) file.writeText("[]")
}
LaunchedEffect(enabled) {
if (enabled) {
clients.clear()
clients.addAll(Json.decodeFromString<List<DhizukuClientInfo>>(file.readText()))
}
}
MyLazyScaffold(R.string.dhizuku_server, onNavigateUp) {
item {
SwitchItem(R.string.enable, getState = { sp.dhizukuServer }, onCheckedChange = ::changeEnableState)
Spacer(Modifier.padding(vertical = 8.dp))
}
if (enabled) itemsIndexed(clients) { index, client ->
val name = pm.getNameForUid(client.uid)
if (name == null) {
clients.dropWhile { it.uid == client.uid }
writeList()
} else {
val info = pm.getApplicationInfo(name, 0)
Row(
Modifier
.fillMaxWidth().padding(8.dp)
.background(colorScheme.surfaceVariant, RoundedCornerShape(8.dp))
.padding(8.dp),
Arrangement.SpaceBetween, Alignment.CenterVertically
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Image(
rememberDrawablePainter(info.loadIcon(pm)), null,
Modifier.padding(end = 12.dp).size(50.dp)
)
Text(info.loadLabel(pm).toString(), style = typography.titleLarge)
}
Switch(client.allow, { clients[index] = client.copy(allow = it) })
}
}
}
}
}
@Serializable object LockScreenInfo @Serializable object LockScreenInfo
@RequiresApi(24) @RequiresApi(24)

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512"
tools:ignore="VectorRaster,VectorPath">
<path
android:pathData="M169.19,346.11C161.37,346.07 159.99,344.74 159.91,337.43C159.86,332.6 159.82,327.76 159.92,322.93C160.03,317.38 162.37,315.03 167.89,314.94C173.56,314.84 179.23,314.79 184.88,314.97C188.39,315.09 189.99,314.12 189.97,310.22C189.85,279.06 189.81,247.9 190.01,216.74C190.04,212.88 187.79,212.53 185.32,211.61C151.63,199.06 142.08,155.85 167.82,131.55C182.14,118.04 199.25,114.18 217.77,120.57C235.72,126.77 246.5,139.95 249.78,158.77C253.66,181.07 240.75,203.1 218.95,211.11C214.46,212.76 212.81,214.83 213.07,219.6C213.5,227.25 213.27,234.93 213.15,242.59C213.09,245.99 214.34,247.35 217.84,247.3C227.83,247.14 237.83,247.33 247.83,247.15C249.44,247.12 251.44,246.36 252.56,245.24C281.47,216.41 310.27,187.48 339.15,158.63C341.21,156.56 341.48,154.77 341.33,151.75C340.74,139.79 340.5,127.75 341.16,115.8C341.59,107.98 350.04,101.51 358.52,101.3C367.18,101.08 375.85,101.06 384.51,101.29C395.41,101.59 403.05,109.67 403.1,120.62C403.14,128.95 403.17,137.28 403.09,145.61C403,156.59 395.44,164.22 384.54,164.07C373.25,163.92 361.97,163.38 350.68,163.14C349.71,163.11 348.51,163.87 347.76,164.61C320.45,191.46 293.18,218.36 265.91,245.26C266.2,245.79 266.49,246.33 266.79,246.86C268.13,246.99 269.47,247.25 270.81,247.25C295.14,247.27 319.47,247.19 343.8,247.33C347.67,247.36 348.96,246.32 348.93,242.29C348.84,229.41 356.49,222.1 369.5,222.09C377.34,222.09 385.17,222.06 393,222.1C403.99,222.16 411.89,230.06 411.93,241.01C411.96,249.18 411.95,257.34 411.93,265.51C411.9,275.98 405.11,284.01 394.76,284.57C385.3,285.08 375.75,284.96 366.3,284.3C355.87,283.58 348.97,275.41 348.83,264.85C348.81,262.88 348.83,260.91 348.83,258.52C320.72,258.52 293.03,258.52 264.72,258.52C265.54,259.71 266.01,260.69 266.73,261.41C293.62,287.99 320.53,314.56 347.49,341.07C348.41,341.98 349.98,342.75 351.23,342.73C363.5,342.5 375.77,341.69 388.03,341.87C395.97,342 402.86,349.81 403.04,357.93C403.26,367.76 403.25,377.6 403.05,387.42C402.87,396.32 395.21,403.9 386.31,404.05C376.65,404.22 366.98,404.23 357.32,404.05C348.72,403.89 340.82,396.19 340.86,387.71C340.92,375.93 341.54,364.15 341.73,352.36C341.75,351.13 340.8,349.6 339.85,348.65C310.24,319.13 280.6,289.63 250.84,260.26C249.52,258.95 247.08,258.24 245.14,258.2C236.31,257.98 227.47,258.36 218.65,258C214.08,257.81 213.1,259.44 213.12,263.71C213.26,303.53 213.27,343.36 213.04,383.18C213.02,386.72 211.8,390.87 209.69,393.63C206.78,397.44 202.06,398.12 197.39,396.23C192.8,394.37 189.92,390.96 190,385.99C190.07,382.15 188.72,381.1 185.08,381.24C178.93,381.49 172.75,381.23 166.59,381.31C161.99,381.37 159.8,379.21 159.9,374.64C159.93,372.98 159.81,371.3 159.92,369.65C160.21,365.26 163.23,362.35 167.69,362.28C173.82,362.2 179.96,362.26 186.44,362.26C186.44,356.74 186.44,351.69 186.44,346.11C180.58,346.11 175.12,346.11 169.19,346.11M225.82,157.02C225.25,155.82 224.75,154.58 224.09,153.43C218.37,143.47 206.71,138.58 195.41,141.36C184.77,143.98 176.26,154.09 176.01,164.59C175.75,175.62 180.5,183.97 190.38,188.78C200.04,193.48 209.59,192.47 217.94,185.64C226.85,178.35 229.1,168.76 225.82,157.02M351.09,144.13C351.42,151.11 353.26,153.78 358.41,153.87C367.22,154.02 376.04,153.98 384.85,153.86C389.4,153.8 392.18,150.94 392.36,146.71C392.73,137.6 392.72,128.45 392.42,119.32C392.25,114.31 389.33,111.97 384.24,111.91C375.92,111.83 367.61,111.79 359.29,111.92C353.69,112.01 351.15,114.71 351.12,120.25C351.07,127.9 351.1,135.55 351.09,144.13M376.45,393.84C378.78,393.84 381.11,393.86 383.44,393.83C390.03,393.75 392.16,391.86 392.28,385.26C392.44,376.61 392.37,367.95 392.21,359.31C392.12,354.45 389.59,352.02 384.7,351.96C376.55,351.86 368.39,351.9 360.24,351.93C353.9,351.95 351.32,354.24 351.19,360.47C351.01,368.79 351.03,377.11 351.17,385.43C351.26,391.07 353.88,393.65 359.49,393.8C364.81,393.94 370.14,393.84 376.45,393.84M374.55,274.12C380.88,274.12 387.22,274.21 393.54,274.1C398.38,274.02 401.11,271.51 401.2,266.77C401.36,257.95 401.34,249.11 401.18,240.29C401.09,235.46 398.41,232.84 393.75,232.8C384.93,232.71 376.1,232.73 367.27,232.79C362.93,232.82 360.24,235.05 360.16,239.46C360,248.45 359.98,257.45 360.16,266.45C360.27,271.4 363.19,273.99 368.08,274.11C369.91,274.16 371.75,274.12 374.55,274.12z"
android:fillColor="#4DCBE4"
android:strokeColor="#00000000" />
</vector>

View File

@@ -106,6 +106,11 @@
<string name="transfer">转移</string> <string name="transfer">转移</string>
<string name="transfer_ownership_warning">%1$s 特权将被转移至 %2$s</string> <string name="transfer_ownership_warning">%1$s 特权将被转移至 %2$s</string>
<string name="dhizuku_server">Dhizuku服务器</string>
<string name="request_permission">请求权限</string>
<string name="allow">允许</string>
<string name="reject">拒绝</string>
<!--Receiver--> <!--Receiver-->
<string name="create_work_profile_success">创建工作资料成功</string> <string name="create_work_profile_success">创建工作资料成功</string>

View File

@@ -112,6 +112,11 @@
<string name="transfer">Transfer</string> <string name="transfer">Transfer</string>
<string name="transfer_ownership_warning">%1$s privilege will be transferred to %2$s</string> <string name="transfer_ownership_warning">%1$s privilege will be transferred to %2$s</string>
<string name="dhizuku_server">Dhizuku server</string>
<string name="request_permission">Request permission</string>
<string name="allow">Allow</string>
<string name="reject">Reject</string>
<!--Receiver--> <!--Receiver-->
<string name="create_work_profile_success">Create work profile success</string> <string name="create_work_profile_success">Create work profile success</string>

View File

@@ -3,12 +3,13 @@ agp = "8.10.0"
kotlin = "2.1.20" kotlin = "2.1.20"
navigation-compose = "2.9.0" navigation-compose = "2.9.0"
composeBom = "2025.05.00" composeBom = "2025.05.01"
accompanist-drawablepainter = "0.35.0-alpha" accompanist-drawablepainter = "0.35.0-alpha"
accompanist-permissions = "0.37.0" accompanist-permissions = "0.37.0"
shizuku = "13.1.5" shizuku = "13.1.5"
fragment = "1.8.6" fragment = "1.8.7"
dhizuku = "2.5.3" dhizuku = "2.5.3"
dhizuku-server = "0.0.5"
hiddenApiBypass = "4.3" hiddenApiBypass = "4.3"
libsu = "6.0.0" libsu = "6.0.0"
serialization = "1.7.3" serialization = "1.7.3"
@@ -28,6 +29,7 @@ accompanist-permissions = { group = "com.google.accompanist", name = "accompanis
shizuku-provider = { module = "dev.rikka.shizuku:provider", version.ref = "shizuku" } shizuku-provider = { module = "dev.rikka.shizuku:provider", version.ref = "shizuku" }
shizuku-api = { module = "dev.rikka.shizuku:api", version.ref = "shizuku" } shizuku-api = { module = "dev.rikka.shizuku:api", version.ref = "shizuku" }
dhizuku-api = { module = "io.github.iamr0s:Dhizuku-API", version.ref = "dhizuku" } dhizuku-api = { module = "io.github.iamr0s:Dhizuku-API", version.ref = "dhizuku" }
dhizuku-server-api = { group = "io.github.iamr0s", name = "Dhizuku-SERVER_API", version.ref = "dhizuku-server" }
hiddenApiBypass = { module = "org.lsposed.hiddenapibypass:hiddenapibypass", version.ref = "hiddenApiBypass" } hiddenApiBypass = { module = "org.lsposed.hiddenapibypass:hiddenapibypass", version.ref = "hiddenApiBypass" }
libsu = { module = "com.github.topjohnwu.libsu:core", version.ref = "libsu" } libsu = { module = "com.github.topjohnwu.libsu:core", version.ref = "libsu" }
libsu-service = { module = "com.github.topjohnwu.libsu:service", version.ref = "libsu" } libsu-service = { module = "com.github.topjohnwu.libsu:service", version.ref = "libsu" }