mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 11:05:59 +00:00
Integrate Dhizuku server
This commit is contained in:
@@ -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>
|
||||
|
||||
147
app/src/main/java/com/bintianqi/owndroid/DhizukuServer.kt
Normal file
147
app/src/main/java/com/bintianqi/owndroid/DhizukuServer.kt
Normal 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
|
||||
)
|
||||
@@ -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) }
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
13
app/src/main/res/drawable/dhizuku_icon.xml
Normal file
13
app/src/main/res/drawable/dhizuku_icon.xml
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user