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.api)
implementation(libs.dhizuku.api)
implementation(libs.dhizuku.server.api)
implementation(libs.androidx.fragment)
implementation(libs.hiddenApiBypass)
implementation(libs.libsu)

View File

@@ -20,3 +20,7 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
# -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:exported="true"
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
android:name=".Receiver"
android:description="@string/app_name"
@@ -97,5 +108,12 @@
android:enabled="true"
android:exported="true"
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>
</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.DeviceInfo
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.DisableAccountManagementScreen
import com.bintianqi.owndroid.dpm.DisableMeteredData
@@ -324,6 +326,7 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
}
}, ::navigate)
}
composable<DhizukuServerSettings> { DhizukuServerSettingsScreen(::navigateUp) }
composable<DelegatedAdmins> { DelegatedAdminsScreen(::navigateUp, ::navigate) }
composable<AddDelegatedAdmin>{ AddDelegatedAdminScreen(it.toRoute(), ::navigateUp) }

View File

@@ -64,7 +64,7 @@ class Receiver : DeviceAdminReceiver() {
super.onSecurityLogsAvailable(context, intent)
if(VERSION.SDK_INT >= 24) {
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 fileExists = file.exists()
file.outputStream().use {

View File

@@ -23,7 +23,8 @@ class SharedPrefs(context: Context) {
var biometricsUnlock by BooleanSharedPref("lock.biometrics")
var lockWhenLeaving by BooleanSharedPref("lock.onleave")
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> {

View File

@@ -2,8 +2,10 @@ package com.bintianqi.owndroid
import android.content.ClipData
import android.content.ClipboardManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInfo
import android.net.Uri
import android.os.Build
import android.os.Bundle
@@ -131,3 +133,13 @@ fun String.hash(): String {
val md = MessageDigest.getInstance("SHA-256")
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.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.toBitmap
import com.bintianqi.owndroid.MyAdminComponent
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.Receiver
import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.ShortcutsReceiverActivity
import com.bintianqi.owndroid.backToHomeStateFlow
import com.bintianqi.owndroid.createShortcuts
import com.bintianqi.owndroid.myPrivilege
import com.rosan.dhizuku.api.Dhizuku
@@ -130,7 +130,7 @@ fun Context.getReceiver(): ComponentName {
return if(SharedPrefs(this).dhizuku) {
Dhizuku.getOwnerComponent()
} 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.RequiresApi
import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
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.fillMaxWidth
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.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
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.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
@@ -84,23 +89,28 @@ import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.lifecycle.compose.collectAsStateWithLifecycle
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.IUserService
import com.bintianqi.owndroid.MyAdminComponent
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.Receiver
import com.bintianqi.owndroid.Settings
import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CircularProgressDialog
import com.bintianqi.owndroid.ui.InfoItem
import com.bintianqi.owndroid.ui.MyLazyScaffold
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.MySmallTitleScaffold
import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.updatePrivilege
import com.bintianqi.owndroid.useShizuku
import com.bintianqi.owndroid.yesOrNo
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import com.rosan.dhizuku.api.Dhizuku
import com.rosan.dhizuku.api.DhizukuRequestPermissionListener
import com.topjohnwu.superuser.Shell
@@ -108,6 +118,8 @@ import com.topjohnwu.superuser.ipc.RootService
import dalvik.system.DexClassLoader
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.lang.invoke.MethodHandles
import java.lang.reflect.Proxy
@@ -258,6 +270,14 @@ fun WorkModesScreen(
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)) {
Row(Modifier.padding(bottom = 4.dp), verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Outlined.Warning, null, Modifier.padding(end = 4.dp), colorScheme.error)
@@ -338,7 +358,7 @@ fun WorkModesScreen(
if(privilege.device) {
dpm.clearDeviceOwnerApp(context.packageName)
} else if(VERSION.SDK_INT >= 24) {
dpm.clearProfileOwner(ComponentName(context, Receiver::class.java))
dpm.clearProfileOwner(MyAdminComponent)
}
}
dialog = 0
@@ -424,10 +444,7 @@ fun activateUsingDhizuku(context: Context, callback: (Boolean, Boolean, String?)
if(dpm == null) {
context.showOperationResultToast(false)
} else {
dpm.transferOwnership(
Dhizuku.getOwnerComponent(),
ComponentName(context, Receiver::class.java), PersistableBundle()
)
dpm.transferOwnership(Dhizuku.getOwnerComponent(), MyAdminComponent, PersistableBundle())
callback(true, true, null)
}
} 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"
@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
@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_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-->
<string name="create_work_profile_success">创建工作资料成功</string>

View File

@@ -112,6 +112,11 @@
<string name="transfer">Transfer</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-->
<string name="create_work_profile_success">Create work profile success</string>