mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 02:56:01 +00:00
Add WorkModesScreen
Update dependency Remove some Shizuku features
This commit is contained in:
@@ -94,6 +94,7 @@ dependencies {
|
||||
implementation(libs.dhizuku.api)
|
||||
implementation(libs.androidx.fragment)
|
||||
implementation(libs.hiddenApiBypass)
|
||||
implementation(libs.libsu)
|
||||
implementation(libs.serialization)
|
||||
implementation(kotlin("reflect"))
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.bintianqi.owndroid;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.os.Bundle;
|
||||
|
||||
interface IUserService {
|
||||
String execute(String command) = 1;
|
||||
int getUid() = 2;
|
||||
Account[] listAccounts() = 3;
|
||||
Bundle execute(String command) = 1;
|
||||
void destroy() = 16777114;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.bintianqi.owndroid
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
@@ -22,14 +21,18 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -57,8 +60,6 @@ import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.dialog
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.toRoute
|
||||
import com.bintianqi.owndroid.dpm.Accounts
|
||||
import com.bintianqi.owndroid.dpm.AccountsScreen
|
||||
import com.bintianqi.owndroid.dpm.AddApnSetting
|
||||
import com.bintianqi.owndroid.dpm.AddApnSettingScreen
|
||||
import com.bintianqi.owndroid.dpm.AddDelegatedAdmin
|
||||
@@ -107,8 +108,6 @@ 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.DeviceOwner
|
||||
import com.bintianqi.owndroid.dpm.DeviceOwnerScreen
|
||||
import com.bintianqi.owndroid.dpm.DisableAccountManagement
|
||||
import com.bintianqi.owndroid.dpm.DisableAccountManagementScreen
|
||||
import com.bintianqi.owndroid.dpm.DisableMeteredData
|
||||
@@ -172,8 +171,6 @@ import com.bintianqi.owndroid.dpm.PreferentialNetworkService
|
||||
import com.bintianqi.owndroid.dpm.PreferentialNetworkServiceScreen
|
||||
import com.bintianqi.owndroid.dpm.PrivateDns
|
||||
import com.bintianqi.owndroid.dpm.PrivateDnsScreen
|
||||
import com.bintianqi.owndroid.dpm.ProfileOwner
|
||||
import com.bintianqi.owndroid.dpm.ProfileOwnerScreen
|
||||
import com.bintianqi.owndroid.dpm.QueryNetworkStats
|
||||
import com.bintianqi.owndroid.dpm.RecommendedGlobalProxy
|
||||
import com.bintianqi.owndroid.dpm.RecommendedGlobalProxyScreen
|
||||
@@ -191,7 +188,6 @@ import com.bintianqi.owndroid.dpm.SecurityLoggingScreen
|
||||
import com.bintianqi.owndroid.dpm.SetDefaultDialer
|
||||
import com.bintianqi.owndroid.dpm.SetDefaultDialerScreen
|
||||
import com.bintianqi.owndroid.dpm.SetSystemUpdatePolicy
|
||||
import com.bintianqi.owndroid.dpm.ShizukuScreen
|
||||
import com.bintianqi.owndroid.dpm.SupportMessage
|
||||
import com.bintianqi.owndroid.dpm.SupportMessageScreen
|
||||
import com.bintianqi.owndroid.dpm.Suspend
|
||||
@@ -230,6 +226,8 @@ import com.bintianqi.owndroid.dpm.WifiSecurityLevelScreen
|
||||
import com.bintianqi.owndroid.dpm.WifiSsidPolicyScreen
|
||||
import com.bintianqi.owndroid.dpm.WipeData
|
||||
import com.bintianqi.owndroid.dpm.WipeDataScreen
|
||||
import com.bintianqi.owndroid.dpm.WorkModes
|
||||
import com.bintianqi.owndroid.dpm.WorkModesScreen
|
||||
import com.bintianqi.owndroid.dpm.WorkProfile
|
||||
import com.bintianqi.owndroid.dpm.WorkProfileScreen
|
||||
import com.bintianqi.owndroid.dpm.dhizukuErrorStatus
|
||||
@@ -299,6 +297,14 @@ fun Home(vm: MyViewModel) {
|
||||
val userRestrictions by vm.userRestrictions.collectAsStateWithLifecycle()
|
||||
fun navigateUp() { navController.navigateUp() }
|
||||
fun navigate(destination: Any) { navController.navigate(destination) }
|
||||
LaunchedEffect(Unit) {
|
||||
val privilege = myPrivilege.value
|
||||
if(!privilege.device && !privilege.profile) {
|
||||
navController.navigate(WorkModes(false)) {
|
||||
popUpTo<Home> { inclusive = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
@Suppress("NewApi") NavHost(
|
||||
navController = navController,
|
||||
startDestination = Home,
|
||||
@@ -313,20 +319,35 @@ fun Home(vm: MyViewModel) {
|
||||
popExitTransition = Animations.navHostPopExitTransition
|
||||
) {
|
||||
composable<Home> { HomeScreen { navController.navigate(it) } }
|
||||
composable<WorkModes> {
|
||||
WorkModesScreen(it.toRoute(), ::navigateUp, {
|
||||
navController.navigate(Home) {
|
||||
popUpTo<WorkModes> { inclusive = true }
|
||||
}
|
||||
}, {
|
||||
navController.navigate(WorkModes(false)) {
|
||||
popUpTo<Home> { inclusive = true }
|
||||
}
|
||||
}, {
|
||||
navController.navigate(it)
|
||||
})
|
||||
}
|
||||
|
||||
composable<Permissions> {
|
||||
PermissionsScreen(::navigateUp, { navController.navigate(it) }) { navController.navigate(ShizukuScreen, it) }
|
||||
PermissionsScreen(::navigateUp) { navController.navigate(it) }
|
||||
}
|
||||
composable<ShizukuScreen> { ShizukuScreen(it.arguments!!, ::navigateUp) { dest -> navController.navigate(dest) } }
|
||||
composable<Accounts>(mapOf(serializableNavTypePair<List<Accounts.Account>>())) { AccountsScreen(it.toRoute(), ::navigateUp) }
|
||||
composable<ProfileOwner> { ProfileOwnerScreen(::navigateUp) }
|
||||
composable<DeviceOwner> { DeviceOwnerScreen(::navigateUp) }
|
||||
composable<DelegatedAdmins> { DelegatedAdminsScreen(::navigateUp, ::navigate) }
|
||||
composable<AddDelegatedAdmin>{ AddDelegatedAdminScreen(it.toRoute(), ::navigateUp) }
|
||||
composable<DeviceInfo> { DeviceInfoScreen(::navigateUp) }
|
||||
composable<LockScreenInfo> { LockScreenInfoScreen(::navigateUp) }
|
||||
composable<SupportMessage> { SupportMessageScreen(::navigateUp) }
|
||||
composable<TransferOwnership> { TransferOwnershipScreen(::navigateUp) }
|
||||
composable<TransferOwnership> {
|
||||
TransferOwnershipScreen(::navigateUp) {
|
||||
navController.navigate(WorkModes(false)) {
|
||||
popUpTo(Home) { inclusive = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
composable<SystemManager> { SystemManagerScreen(::navigateUp, ::navigate) }
|
||||
composable<SystemOptions> { SystemOptionsScreen(::navigateUp) }
|
||||
@@ -492,10 +513,10 @@ fun Home(vm: MyViewModel) {
|
||||
|
||||
@Serializable private object Home
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun HomeScreen(onNavigate: (Any) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
val privilege by myPrivilege.collectAsStateWithLifecycle()
|
||||
val activateType = (if(privilege.dhizuku) context.getString(R.string.dhizuku) + " - " else "") +
|
||||
context.getString(
|
||||
@@ -504,9 +525,18 @@ private fun HomeScreen(onNavigate: (Any) -> Unit) {
|
||||
else if(privilege.profile) R.string.profile_owner
|
||||
else R.string.click_to_activate
|
||||
)
|
||||
Scaffold {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
{}, actions = {
|
||||
IconButton({ onNavigate(WorkModes(true)) }) { Icon(painterResource(R.drawable.security_fill0), null) }
|
||||
IconButton({ onNavigate(Settings) }) { Icon(Icons.Default.Settings, null) }
|
||||
}
|
||||
)
|
||||
}
|
||||
) {
|
||||
Column(modifier = Modifier.padding(it).verticalScroll(rememberScrollState())) {
|
||||
Spacer(Modifier.padding(vertical = 25.dp))
|
||||
Spacer(Modifier.padding(vertical = 8.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.app_name), style = typography.headlineLarge,
|
||||
modifier = Modifier.padding(start = HorizontalPadding)
|
||||
@@ -541,16 +571,9 @@ private fun HomeScreen(onNavigate: (Any) -> Unit) {
|
||||
HomePageItem(R.string.system, R.drawable.android_fill0) { onNavigate(SystemManager) }
|
||||
HomePageItem(R.string.network, R.drawable.wifi_fill0) { onNavigate(Network) }
|
||||
}
|
||||
if(
|
||||
privilege.work || (VERSION.SDK_INT < 24 && !privilege.device) ||
|
||||
dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)
|
||||
) {
|
||||
if(privilege.work) {
|
||||
HomePageItem(R.string.work_profile, R.drawable.work_fill0) {
|
||||
onNavigate(
|
||||
if(VERSION.SDK_INT < 24 ||
|
||||
dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)
|
||||
) WorkProfile else CreateWorkProfile
|
||||
)
|
||||
onNavigate(WorkProfile)
|
||||
}
|
||||
}
|
||||
if(privilege.device || privilege.profile) {
|
||||
@@ -563,7 +586,6 @@ private fun HomeScreen(onNavigate: (Any) -> Unit) {
|
||||
HomePageItem(R.string.users,R.drawable.manage_accounts_fill0) { onNavigate(Users) }
|
||||
HomePageItem(R.string.password_and_keyguard, R.drawable.password_fill0) { onNavigate(Password) }
|
||||
}
|
||||
HomePageItem(R.string.settings, R.drawable.settings_fill0) { onNavigate(Settings) }
|
||||
Spacer(Modifier.padding(vertical = 20.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.bintianqi.owndroid.ui.FunctionItem
|
||||
import com.bintianqi.owndroid.ui.MyScaffold
|
||||
import com.bintianqi.owndroid.ui.Notes
|
||||
@@ -60,6 +61,7 @@ import java.util.Locale
|
||||
@Composable
|
||||
fun SettingsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val privilege by myPrivilege.collectAsStateWithLifecycle()
|
||||
val exportLogsLauncher = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) {
|
||||
if(it != null) exportLogs(context, it)
|
||||
}
|
||||
@@ -67,7 +69,9 @@ fun SettingsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
||||
FunctionItem(title = R.string.options, icon = R.drawable.tune_fill0) { onNavigate(SettingsOptions) }
|
||||
FunctionItem(title = R.string.appearance, icon = R.drawable.format_paint_fill0) { onNavigate(Appearance) }
|
||||
FunctionItem(R.string.app_lock, icon = R.drawable.lock_fill0) { onNavigate(AppLockSettings) }
|
||||
if (privilege.device || privilege.profile)
|
||||
FunctionItem(title = R.string.api, icon = R.drawable.code_fill0) { onNavigate(ApiSettings) }
|
||||
if (privilege.device && !privilege.dhizuku)
|
||||
FunctionItem(R.string.notifications, icon = R.drawable.notifications_fill0) { onNavigate(Notifications) }
|
||||
FunctionItem(title = R.string.export_logs, icon = R.drawable.description_fill0) {
|
||||
val time = SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(Date(System.currentTimeMillis()))
|
||||
|
||||
72
app/src/main/java/com/bintianqi/owndroid/ShizukuService.kt
Normal file
72
app/src/main/java/com/bintianqi/owndroid/ShizukuService.kt
Normal file
@@ -0,0 +1,72 @@
|
||||
package com.bintianqi.owndroid
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.ServiceConnection
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.Keep
|
||||
import rikka.shizuku.Shizuku
|
||||
import rikka.sui.Sui
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
@Keep
|
||||
class ShizukuService: IUserService.Stub() {
|
||||
override fun execute(command: String): Bundle? {
|
||||
try {
|
||||
val bundle = Bundle()
|
||||
val process = Runtime.getRuntime().exec(command)
|
||||
val exitCode = process.waitFor()
|
||||
bundle.putInt("code", exitCode)
|
||||
bundle.putString("output", process.inputStream.readBytes().decodeToString())
|
||||
bundle.putString("error", process.errorStream.readBytes().decodeToString())
|
||||
return bundle
|
||||
} catch(e: Exception) {
|
||||
e.printStackTrace()
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
override fun destroy() {
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
||||
|
||||
fun getShizukuArgs(context: Context): Shizuku.UserServiceArgs {
|
||||
return Shizuku.UserServiceArgs(ComponentName(context, ShizukuService::class.java))
|
||||
.daemon(false)
|
||||
.processNameSuffix("shizuku-service")
|
||||
.debuggable(false)
|
||||
.version(1)
|
||||
}
|
||||
|
||||
fun useShizuku(context: Context, action: (IBinder?) -> Unit) {
|
||||
val connection = object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
action(service)
|
||||
Shizuku.unbindUserService(getShizukuArgs(context), this, true)
|
||||
}
|
||||
override fun onServiceDisconnected(name: ComponentName?) {}
|
||||
}
|
||||
try {
|
||||
if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
|
||||
Shizuku.bindUserService(getShizukuArgs(context), connection)
|
||||
} else if(Shizuku.shouldShowRequestPermissionRationale()) {
|
||||
Toast.makeText(context, R.string.permission_denied, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Sui.init(context.packageName)
|
||||
fun requestPermissionResultListener(requestCode: Int, grantResult: Int) {
|
||||
if(grantResult != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(context, R.string.permission_denied, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
Shizuku.removeRequestPermissionResultListener(::requestPermissionResultListener)
|
||||
}
|
||||
Shizuku.addRequestPermissionResultListener(::requestPermissionResultListener)
|
||||
Shizuku.requestPermission(0)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
@@ -1062,7 +1062,7 @@ fun EnableSystemAppScreen(onNavigateUp: () -> Unit) {
|
||||
) {
|
||||
Text(stringResource(R.string.enable))
|
||||
}
|
||||
Notes(R.string.enable_system_app)
|
||||
Notes(R.string.info_enable_system_app)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,17 +61,8 @@ val Context.isProfileOwner: Boolean
|
||||
return dpm.isProfileOwnerApp("com.bintianqi.owndroid")
|
||||
}
|
||||
|
||||
val Context.dpcPackageName: String
|
||||
get() {
|
||||
return if(SharedPrefs(this).dhizuku) {
|
||||
Dhizuku.getOwnerPackageName()
|
||||
} else {
|
||||
"com.bintianqi.owndroid"
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("PrivateApi")
|
||||
private fun binderWrapperDevicePolicyManager(appContext: Context): DevicePolicyManager? {
|
||||
fun binderWrapperDevicePolicyManager(appContext: Context): DevicePolicyManager? {
|
||||
try {
|
||||
val context = appContext.createPackageContext(Dhizuku.getOwnerComponent().packageName, Context.CONTEXT_IGNORE_SECURITY)
|
||||
val manager = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
|
||||
@@ -4,31 +4,67 @@ import android.app.admin.DevicePolicyManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Binder
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.os.RemoteException
|
||||
import android.os.PersistableBundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.annotation.Keep
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.outlined.Edit
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material.icons.outlined.Warning
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.painterResource
|
||||
@@ -37,30 +73,36 @@ import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.bintianqi.owndroid.ChoosePackageContract
|
||||
import com.bintianqi.owndroid.HorizontalPadding
|
||||
import com.bintianqi.owndroid.IUserService
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.Receiver
|
||||
import com.bintianqi.owndroid.Settings
|
||||
import com.bintianqi.owndroid.SharedPrefs
|
||||
import com.bintianqi.owndroid.backToHomeStateFlow
|
||||
import com.bintianqi.owndroid.myPrivilege
|
||||
import com.bintianqi.owndroid.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.*
|
||||
import com.bintianqi.owndroid.ui.FunctionItem
|
||||
import com.bintianqi.owndroid.ui.InfoItem
|
||||
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.updatePrivilege
|
||||
import com.bintianqi.owndroid.useShizuku
|
||||
import com.bintianqi.owndroid.writeClipBoard
|
||||
import com.bintianqi.owndroid.yesOrNo
|
||||
import com.rosan.dhizuku.api.Dhizuku
|
||||
import com.rosan.dhizuku.api.DhizukuRequestPermissionListener
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.Serializable
|
||||
import rikka.shizuku.Shizuku
|
||||
import rikka.sui.Sui
|
||||
|
||||
@Serializable object Permissions
|
||||
|
||||
@Composable
|
||||
fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateToShizuku: (Bundle) -> Unit) {
|
||||
fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
val receiver = context.getReceiver()
|
||||
@@ -69,57 +111,6 @@ fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNav
|
||||
var bindingShizuku by remember { mutableStateOf(false) }
|
||||
val enrollmentSpecificId = if(VERSION.SDK_INT >= 31 && (privilege.device || privilege.profile)) dpm.enrollmentSpecificId else ""
|
||||
MyScaffold(R.string.permissions, onNavigateUp, 0.dp) {
|
||||
if(!dpm.isDeviceOwnerApp(context.packageName)) {
|
||||
SwitchItem(
|
||||
R.string.dhizuku,
|
||||
getState = { SharedPrefs(context).dhizuku },
|
||||
onCheckedChange = { toggleDhizukuMode(it, context) },
|
||||
onClickBlank = { dialog = 4 }
|
||||
)
|
||||
}
|
||||
if(privilege.profile || !privilege.primary) {
|
||||
FunctionItem(
|
||||
R.string.profile_owner, stringResource(if(privilege.profile) R.string.activated else R.string.deactivated),
|
||||
operation = { onNavigate(ProfileOwner) }
|
||||
)
|
||||
}
|
||||
if(!privilege.profile && privilege.primary) {
|
||||
FunctionItem(
|
||||
R.string.device_owner, stringResource(if(privilege.device) R.string.activated else R.string.deactivated),
|
||||
operation = { onNavigate(DeviceOwner) }
|
||||
)
|
||||
}
|
||||
FunctionItem(R.string.shizuku) {
|
||||
try {
|
||||
if(Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
|
||||
bindingShizuku = true
|
||||
fun onBind(binder: IBinder) {
|
||||
bindingShizuku = false
|
||||
onNavigateToShizuku(bundleOf("binder" to binder))
|
||||
}
|
||||
try {
|
||||
controlShizukuService(context, ::onBind, { bindingShizuku = false }, true)
|
||||
} catch(e: Exception) {
|
||||
e.printStackTrace()
|
||||
bindingShizuku = false
|
||||
}
|
||||
} else if(Shizuku.shouldShowRequestPermissionRationale()) {
|
||||
Toast.makeText(context, R.string.permission_denied, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Sui.init(context.packageName)
|
||||
fun requestPermissionResultListener(requestCode: Int, grantResult: Int) {
|
||||
if(grantResult != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(context, R.string.permission_denied, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
Shizuku.removeRequestPermissionResultListener(::requestPermissionResultListener)
|
||||
}
|
||||
Shizuku.addRequestPermissionResultListener(::requestPermissionResultListener)
|
||||
Shizuku.requestPermission(0)
|
||||
}
|
||||
} catch(_: IllegalStateException) {
|
||||
Toast.makeText(context, R.string.shizuku_not_started, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
if(VERSION.SDK_INT >= 26) FunctionItem(R.string.delegated_admins) { onNavigate(DelegatedAdmins) }
|
||||
FunctionItem(R.string.device_info, icon = R.drawable.perm_device_information_fill0) { onNavigate(DeviceInfo) }
|
||||
if(VERSION.SDK_INT >= 24 && (privilege.profile || (VERSION.SDK_INT >= 26 && privilege.device))) {
|
||||
@@ -137,9 +128,6 @@ fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNav
|
||||
if(VERSION.SDK_INT >= 24) {
|
||||
FunctionItem(R.string.support_messages, icon = R.drawable.chat_fill0) { onNavigate(SupportMessage) }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28) {
|
||||
FunctionItem(R.string.transfer_ownership, icon = R.drawable.admin_panel_settings_fill0) { onNavigate(TransferOwnership) }
|
||||
}
|
||||
}
|
||||
if(bindingShizuku) {
|
||||
Dialog(onDismissRequest = { bindingShizuku = false }) {
|
||||
@@ -225,38 +213,328 @@ fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNav
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleDhizukuMode(status: Boolean, context: Context) {
|
||||
val sp = SharedPrefs(context)
|
||||
if(!status) {
|
||||
sp.dhizuku = false
|
||||
backToHomeStateFlow.value = true
|
||||
return
|
||||
@Serializable data class WorkModes(val canNavigateUp: Boolean)
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun WorkModesScreen(
|
||||
params: WorkModes, onNavigateUp: () -> Unit, onActivate: () -> Unit, onDeactivate: () -> Unit,
|
||||
onNavigate: (Any) -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val coroutine = rememberCoroutineScope()
|
||||
val privilege by myPrivilege.collectAsStateWithLifecycle()
|
||||
/** 0: none, 1: device owner, 2: circular progress indicator, 3: result, 4: deactivate, 5: command */
|
||||
var dialog by remember { mutableIntStateOf(0) }
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
{
|
||||
if(!params.canNavigateUp) {
|
||||
Column {
|
||||
Text(stringResource(R.string.app_name))
|
||||
Text(stringResource(R.string.choose_work_mode), Modifier.alpha(0.8F), style = typography.bodyLarge)
|
||||
}
|
||||
if(!Dhizuku.init(context)) {
|
||||
dhizukuErrorStatus.value = 1
|
||||
return
|
||||
}
|
||||
if(dhizukuPermissionGranted()) {
|
||||
sp.dhizuku = true
|
||||
Dhizuku.init(context)
|
||||
},
|
||||
navigationIcon = {
|
||||
if(params.canNavigateUp) NavIcon(onNavigateUp)
|
||||
},
|
||||
actions = {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
if(privilege.device || privilege.profile) Box {
|
||||
IconButton({ expanded = true }) {
|
||||
Icon(Icons.Default.MoreVert, null)
|
||||
}
|
||||
DropdownMenu(expanded, { expanded = false }) {
|
||||
DropdownMenuItem({ Text(stringResource(R.string.deactivate)) }, { dialog = 4 })
|
||||
if(!privilege.dhizuku && VERSION.SDK_INT >= 28) DropdownMenuItem(
|
||||
{ Text(stringResource(R.string.transfer_ownership)) },
|
||||
{
|
||||
expanded = false
|
||||
onNavigate(TransferOwnership)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
if(!params.canNavigateUp) IconButton({ onNavigate(Settings) }) {
|
||||
Icon(Icons.Default.Settings, null)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
var operationSucceed by remember { mutableStateOf(false) }
|
||||
var resultText by remember { mutableStateOf("") }
|
||||
fun handleResult(succeeded: Boolean, activateSucceeded: Boolean, output: String?) {
|
||||
if(succeeded) {
|
||||
operationSucceed = activateSucceeded
|
||||
resultText = output ?: ""
|
||||
dialog = 3
|
||||
updatePrivilege(context)
|
||||
backToHomeStateFlow.value = true
|
||||
handlePrivilegeChange(context)
|
||||
} else {
|
||||
context.showOperationResultToast(false)
|
||||
}
|
||||
}
|
||||
Column(Modifier.fillMaxSize().padding(paddingValues)) {
|
||||
if(!privilege.profile && (VERSION.SDK_INT >= 28 || !privilege.dhizuku)) Row(
|
||||
Modifier
|
||||
.fillMaxWidth().clickable(!privilege.device || privilege.dhizuku) { dialog = 1 }
|
||||
.background(if(privilege.device) colorScheme.primaryContainer else Color.Transparent)
|
||||
.padding(HorizontalPadding, 10.dp),
|
||||
Arrangement.SpaceBetween, Alignment.CenterVertically
|
||||
) {
|
||||
Column {
|
||||
Text(stringResource(R.string.device_owner), style = typography.titleLarge)
|
||||
if(!privilege.device || privilege.dhizuku) Text(
|
||||
stringResource(R.string.recommended), color = colorScheme.primary, style = typography.labelLarge
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
if(privilege.device) Icons.Default.Check else Icons.AutoMirrored.Default.KeyboardArrowRight, null,
|
||||
tint = if(privilege.device) colorScheme.primary else colorScheme.onBackground
|
||||
)
|
||||
}
|
||||
if(privilege.profile) Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.background(if(privilege.device) colorScheme.primaryContainer else Color.Transparent)
|
||||
.padding(HorizontalPadding, 10.dp),
|
||||
Arrangement.SpaceBetween, Alignment.CenterVertically
|
||||
) {
|
||||
Column {
|
||||
Text(stringResource(R.string.profile_owner), style = typography.titleLarge)
|
||||
}
|
||||
Icon(
|
||||
if(privilege.device) Icons.Default.Check else Icons.AutoMirrored.Default.KeyboardArrowRight, null,
|
||||
tint = if(privilege.device) colorScheme.primary else colorScheme.onBackground
|
||||
)
|
||||
}
|
||||
if(privilege.dhizuku || !(privilege.device || privilege.profile)) Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(!privilege.dhizuku) {
|
||||
dialog = 2
|
||||
activateDhizukuMode(context, ::handleResult)
|
||||
}
|
||||
.background(if(privilege.dhizuku) colorScheme.primaryContainer else Color.Transparent)
|
||||
.padding(HorizontalPadding, 10.dp),
|
||||
Arrangement.SpaceBetween, Alignment.CenterVertically
|
||||
) {
|
||||
Text(stringResource(R.string.dhizuku), style = typography.titleLarge)
|
||||
Icon(
|
||||
if(privilege.dhizuku) Icons.Default.Check else Icons.AutoMirrored.Default.KeyboardArrowRight, null,
|
||||
tint = if(privilege.dhizuku) colorScheme.primary else colorScheme.onBackground
|
||||
)
|
||||
}
|
||||
if(
|
||||
privilege.work || (VERSION.SDK_INT < 24 ||
|
||||
context.getDPM().isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE))
|
||||
) Row(
|
||||
Modifier
|
||||
.fillMaxWidth().clickable(!privilege.work) { onNavigate(CreateWorkProfile) }
|
||||
.background(if(privilege.device) colorScheme.primaryContainer else Color.Transparent)
|
||||
.padding(HorizontalPadding, 10.dp),
|
||||
Arrangement.SpaceBetween, Alignment.CenterVertically
|
||||
) {
|
||||
Column {
|
||||
Text(stringResource(R.string.work_profile), style = typography.titleLarge)
|
||||
}
|
||||
Icon(
|
||||
if(privilege.work) Icons.Default.Check else Icons.AutoMirrored.Default.KeyboardArrowRight, null,
|
||||
tint = if(privilege.device) colorScheme.primary else colorScheme.onBackground
|
||||
)
|
||||
}
|
||||
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)
|
||||
Text(stringResource(R.string.warning), color = colorScheme.error, style = typography.labelLarge)
|
||||
}
|
||||
Text(stringResource(R.string.owndroid_warning))
|
||||
}
|
||||
}
|
||||
if(dialog == 1) AlertDialog(
|
||||
title = { Text(stringResource(R.string.activate_method)) },
|
||||
text = {
|
||||
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
if(!privilege.dhizuku) Button({
|
||||
dialog = 2
|
||||
coroutine.launch {
|
||||
activateUsingShizuku(context, ::handleResult)
|
||||
}
|
||||
}) {
|
||||
Text(stringResource(R.string.shizuku))
|
||||
}
|
||||
if(!privilege.dhizuku) Button({
|
||||
dialog = 2
|
||||
activateUsingRoot(context, ::handleResult)
|
||||
}) {
|
||||
Text("Root")
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28) Button({
|
||||
dialog = 2
|
||||
activateUsingDhizuku(context, ::handleResult)
|
||||
}) {
|
||||
Text(stringResource(R.string.dhizuku))
|
||||
}
|
||||
Button({ dialog = 5 }) { Text(stringResource(R.string.adb_command)) }
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton({ dialog = 0 }) { Text(stringResource(R.string.cancel)) }
|
||||
},
|
||||
onDismissRequest = { dialog = 0 }
|
||||
)
|
||||
if(dialog == 2) Dialog({}) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
if(dialog == 3) AlertDialog(
|
||||
title = { Text(stringResource(if(operationSucceed) R.string.succeeded else R.string.failed)) },
|
||||
text = {
|
||||
Column(Modifier.fillMaxWidth().verticalScroll(rememberScrollState())) {
|
||||
Text(resultText)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton({
|
||||
dialog = 0
|
||||
if(operationSucceed && !params.canNavigateUp) onActivate()
|
||||
}) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
},
|
||||
onDismissRequest = {}
|
||||
)
|
||||
if(dialog == 4) AlertDialog(
|
||||
title = { Text(stringResource(R.string.deactivate)) },
|
||||
text = { Text(stringResource(R.string.info_deactivate)) },
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
{
|
||||
if(privilege.dhizuku) {
|
||||
SharedPrefs(context).dhizuku = false
|
||||
} else {
|
||||
val dpm = context.getDPM()
|
||||
if(privilege.device) {
|
||||
dpm.clearDeviceOwnerApp(context.packageName)
|
||||
} else if(VERSION.SDK_INT >= 24) {
|
||||
dpm.clearProfileOwner(ComponentName(context, Receiver::class.java))
|
||||
}
|
||||
}
|
||||
updatePrivilege(context)
|
||||
handlePrivilegeChange(context)
|
||||
onDeactivate()
|
||||
},
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error)
|
||||
) { Text(stringResource(R.string.confirm)) }
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton({ dialog = 0 }) { Text(stringResource(R.string.cancel)) }
|
||||
},
|
||||
onDismissRequest = { dialog = 0 }
|
||||
)
|
||||
if(dialog == 5) AlertDialog(
|
||||
text = {
|
||||
SelectionContainer {
|
||||
Text(ACTIVATE_DEVICE_OWNER_COMMAND)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton({ dialog = 0 }) { Text(stringResource(R.string.confirm)) }
|
||||
},
|
||||
onDismissRequest = { dialog = 0 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun activateUsingShizuku(context: Context, callback: (Boolean, Boolean, String?) -> Unit) {
|
||||
useShizuku(context) { service ->
|
||||
try {
|
||||
val result = IUserService.Stub.asInterface(service).execute(ACTIVATE_DEVICE_OWNER_COMMAND)
|
||||
if (result == null) {
|
||||
callback(false, false, null)
|
||||
} else {
|
||||
callback(
|
||||
true, result.getInt("code", -1) == 0,
|
||||
result.getString("output") + "\n" + result.getString("error")
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
callback(false, false, null)
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun activateUsingRoot(context: Context, callback: (Boolean, 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")
|
||||
callback(true, result.isSuccess, output)
|
||||
} else {
|
||||
callback(true, false, context.getString(R.string.permission_denied))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(28)
|
||||
fun activateUsingDhizuku(context: Context, callback: (Boolean, Boolean, String?) -> Unit) {
|
||||
fun doTransfer() {
|
||||
try {
|
||||
val dpm = binderWrapperDevicePolicyManager(context)
|
||||
if(dpm == null) {
|
||||
context.showOperationResultToast(false)
|
||||
} else {
|
||||
dpm.transferOwnership(
|
||||
Dhizuku.getOwnerComponent(),
|
||||
ComponentName(context, Receiver::class.java), PersistableBundle()
|
||||
)
|
||||
callback(true, true, null)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
callback(true, false, null)
|
||||
}
|
||||
}
|
||||
if(Dhizuku.init()) {
|
||||
if(Dhizuku.isPermissionGranted()) {
|
||||
doTransfer()
|
||||
} else {
|
||||
Dhizuku.requestPermission(object : DhizukuRequestPermissionListener() {
|
||||
@Throws(RemoteException::class)
|
||||
override fun onRequestPermission(grantResult: Int) {
|
||||
if(grantResult == PackageManager.PERMISSION_GRANTED) {
|
||||
sp.dhizuku = true
|
||||
Dhizuku.init(context)
|
||||
updatePrivilege(context)
|
||||
backToHomeStateFlow.value = true
|
||||
} else {
|
||||
dhizukuErrorStatus.value = 2
|
||||
}
|
||||
if(grantResult == PackageManager.PERMISSION_GRANTED) doTransfer()
|
||||
else callback(false, false, null)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
callback(true, false, context.getString(R.string.failed_to_init_dhizuku))
|
||||
}
|
||||
}
|
||||
|
||||
fun activateDhizukuMode(context: Context, callback: (Boolean, Boolean, String?) -> Unit) {
|
||||
fun onSucceed() {
|
||||
SharedPrefs(context).dhizuku = true
|
||||
callback(true, true, null)
|
||||
}
|
||||
if(Dhizuku.init()) {
|
||||
if(Dhizuku.isPermissionGranted()) {
|
||||
onSucceed()
|
||||
} else {
|
||||
Dhizuku.requestPermission(object : DhizukuRequestPermissionListener() {
|
||||
override fun onRequestPermission(grantResult: Int) {
|
||||
if(grantResult == PackageManager.PERMISSION_GRANTED) onSucceed()
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
callback(true, false, context.getString(R.string.failed_to_init_dhizuku))
|
||||
}
|
||||
}
|
||||
|
||||
const val ACTIVATE_DEVICE_OWNER_COMMAND = "dpm set-device-owner com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver"
|
||||
|
||||
@Serializable object LockScreenInfo
|
||||
|
||||
@@ -302,135 +580,6 @@ fun LockScreenInfoScreen(onNavigateUp: () -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable object ProfileOwner
|
||||
|
||||
@Composable
|
||||
fun ProfileOwnerScreen(onNavigateUp: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
val receiver = context.getReceiver()
|
||||
var deactivateDialog by remember { mutableStateOf(false) }
|
||||
val privilege by myPrivilege.collectAsStateWithLifecycle()
|
||||
val profileOwner = privilege.profile
|
||||
MyScaffold(R.string.profile_owner, onNavigateUp) {
|
||||
Text(stringResource(if(profileOwner) R.string.activated else R.string.deactivated), style = typography.titleLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(VERSION.SDK_INT >= 24 && profileOwner) {
|
||||
Button(
|
||||
onClick = { deactivateDialog = true },
|
||||
enabled = !dpm.isManagedProfile(receiver),
|
||||
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError)
|
||||
) {
|
||||
Text(stringResource(R.string.deactivate))
|
||||
}
|
||||
}
|
||||
if(!profileOwner) {
|
||||
val command = context.getString(R.string.activate_profile_owner_command, (Binder.getCallingUid() / 100000).toString())
|
||||
SelectionContainer {
|
||||
Text(command)
|
||||
}
|
||||
CopyTextButton(R.string.copy_command, command)
|
||||
}
|
||||
}
|
||||
if(deactivateDialog && VERSION.SDK_INT >= 24) {
|
||||
AlertDialog(
|
||||
title = { Text(stringResource(R.string.deactivate)) },
|
||||
onDismissRequest = { deactivateDialog = false },
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = { deactivateDialog = false }
|
||||
) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
dpm.clearProfileOwner(receiver)
|
||||
deactivateDialog = false
|
||||
},
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error)
|
||||
) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable object DeviceOwner
|
||||
|
||||
@Composable
|
||||
fun DeviceOwnerScreen(onNavigateUp: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
var deactivateDialog by remember { mutableStateOf(false) }
|
||||
val privilege by myPrivilege.collectAsStateWithLifecycle()
|
||||
val deviceOwner = privilege.device
|
||||
MyScaffold(R.string.device_owner, onNavigateUp) {
|
||||
Text(text = stringResource(if(deviceOwner) R.string.activated else R.string.deactivated), style = typography.titleLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
AnimatedVisibility(deviceOwner) {
|
||||
Button(
|
||||
onClick = { deactivateDialog = true },
|
||||
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError)
|
||||
) {
|
||||
Text(text = stringResource(R.string.deactivate))
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(!deviceOwner) {
|
||||
Column {
|
||||
SelectionContainer{
|
||||
Text(text = stringResource(R.string.activate_device_owner_command))
|
||||
}
|
||||
CopyTextButton(R.string.copy_command, stringResource(R.string.activate_device_owner_command))
|
||||
}
|
||||
}
|
||||
}
|
||||
if(deactivateDialog) {
|
||||
val sp = SharedPrefs(context)
|
||||
var resetPolicy by remember { mutableStateOf(false) }
|
||||
val coroutine = rememberCoroutineScope()
|
||||
AlertDialog(
|
||||
title = { Text(stringResource(R.string.deactivate)) },
|
||||
text = {
|
||||
Column {
|
||||
if(sp.dhizuku) Text(stringResource(R.string.dhizuku_will_be_deactivated))
|
||||
Spacer(Modifier.padding(vertical = 4.dp))
|
||||
CheckBoxItem(text = R.string.reset_device_policy, checked = resetPolicy, operation = { resetPolicy = it })
|
||||
}
|
||||
},
|
||||
onDismissRequest = { deactivateDialog = false },
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = { deactivateDialog = false }
|
||||
) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
coroutine.launch {
|
||||
if(resetPolicy) context.resetDevicePolicy()
|
||||
dpm.clearDeviceOwnerApp(context.dpcPackageName)
|
||||
if(sp.dhizuku) {
|
||||
if (!Dhizuku.init(context)) {
|
||||
sp.dhizuku = false
|
||||
backToHomeStateFlow.value = true
|
||||
}
|
||||
}
|
||||
deactivateDialog = false
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Keep
|
||||
@Suppress("InlinedApi")
|
||||
enum class DelegatedScope(val id: String, @StringRes val string: Int, val requiresApi: Int = 0) {
|
||||
@@ -698,7 +847,7 @@ fun SupportMessageScreen(onNavigateUp: () -> Unit) {
|
||||
|
||||
@RequiresApi(28)
|
||||
@Composable
|
||||
fun TransferOwnershipScreen(onNavigateUp: () -> Unit) {
|
||||
fun TransferOwnershipScreen(onNavigateUp: () -> Unit, onTransferred: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val privilege by myPrivilege.collectAsStateWithLifecycle()
|
||||
val focusMgr = LocalFocusManager.current
|
||||
@@ -742,7 +891,7 @@ fun TransferOwnershipScreen(onNavigateUp: () -> Unit) {
|
||||
context.showOperationResultToast(true)
|
||||
updatePrivilege(context)
|
||||
dialog = false
|
||||
onNavigateUp()
|
||||
onTransferred()
|
||||
} catch(e: Exception) {
|
||||
e.printStackTrace()
|
||||
context.showOperationResultToast(false)
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.ServiceConnection
|
||||
import android.os.Binder
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.bintianqi.owndroid.IUserService
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.myPrivilege
|
||||
import com.bintianqi.owndroid.ui.MySmallTitleScaffold
|
||||
import com.bintianqi.owndroid.updatePrivilege
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.Serializable
|
||||
import rikka.shizuku.Shizuku
|
||||
|
||||
@Serializable object ShizukuScreen
|
||||
|
||||
@Composable
|
||||
fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccountsViewer: (Accounts) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val privilege by myPrivilege.collectAsStateWithLifecycle()
|
||||
val coroutine = rememberCoroutineScope()
|
||||
val outputTextScrollState = rememberScrollState()
|
||||
var outputText by rememberSaveable { mutableStateOf("") }
|
||||
val binder = navArgs.getBinder("binder")!!
|
||||
var service by remember { mutableStateOf<IUserService?>(null) }
|
||||
LaunchedEffect(Unit) {
|
||||
service = if(binder.pingBinder()) {
|
||||
IUserService.Stub.asInterface(binder)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
MySmallTitleScaffold(R.string.shizuku, onNavigateUp, 0.dp) {
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
coroutine.launch{
|
||||
outputText = service!!.execute("dpm list-owners")
|
||||
outputTextScrollState.animateScrollTo(0)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
) {
|
||||
Text(text = stringResource(R.string.list_owners))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
coroutine.launch{
|
||||
outputText = service!!.execute("pm list users")
|
||||
outputTextScrollState.animateScrollTo(0)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
) {
|
||||
Text(text = stringResource(R.string.list_users))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
Log.d("Shizuku", "List accounts")
|
||||
try {
|
||||
val accounts = service!!.listAccounts().map {
|
||||
Accounts.Account(it.type, it.name)
|
||||
}
|
||||
onNavigateToAccountsViewer(Accounts(accounts))
|
||||
} catch(_: Exception) {
|
||||
outputText = service!!.execute("dumpsys account")
|
||||
coroutine.launch{
|
||||
outputTextScrollState.animateScrollTo(0)
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
) {
|
||||
Text(text = stringResource(R.string.list_accounts))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
|
||||
AnimatedVisibility(!privilege.device, modifier = Modifier.align(Alignment.CenterHorizontally)) {
|
||||
Button(
|
||||
onClick = {
|
||||
coroutine.launch{
|
||||
outputText = service!!.execute(context.getString(R.string.dpm_activate_do_command))
|
||||
outputTextScrollState.animateScrollTo(0)
|
||||
delay(500)
|
||||
updatePrivilege(context)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(text = stringResource(R.string.activate_device_owner))
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(VERSION.SDK_INT >= 30 && privilege.work && !privilege.org) {
|
||||
Button(
|
||||
onClick = {
|
||||
coroutine.launch{
|
||||
val userID = Binder.getCallingUid() / 100000
|
||||
outputText = service!!.execute(
|
||||
"dpm mark-profile-owner-on-organization-owned-device --user $userID com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver"
|
||||
)
|
||||
outputTextScrollState.animateScrollTo(0)
|
||||
delay(500)
|
||||
updatePrivilege(context)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
) {
|
||||
Text(text = stringResource(R.string.activate_org_profile))
|
||||
}
|
||||
}
|
||||
|
||||
SelectionContainer(modifier = Modifier.fillMaxWidth().horizontalScroll(outputTextScrollState)) {
|
||||
Text(text = outputText, softWrap = false, modifier = Modifier.padding(vertical = 9.dp, horizontal = 12.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun controlShizukuService(
|
||||
context: Context,
|
||||
onServiceConnected: (IBinder) -> Unit,
|
||||
onServiceDisconnected: () -> Unit,
|
||||
state: Boolean
|
||||
) {
|
||||
val userServiceConnection = object : ServiceConnection {
|
||||
override fun onServiceConnected(componentName: ComponentName, binder: IBinder) {
|
||||
onServiceConnected(binder)
|
||||
}
|
||||
override fun onServiceDisconnected(componentName: ComponentName) {
|
||||
onServiceDisconnected()
|
||||
}
|
||||
}
|
||||
val userServiceArgs = Shizuku.UserServiceArgs(ComponentName(context, ShizukuService::class.java))
|
||||
.daemon(false)
|
||||
.processNameSuffix("shizuku-service")
|
||||
.debuggable(false)
|
||||
.version(26)
|
||||
if(state) Shizuku.bindUserService(userServiceArgs, userServiceConnection)
|
||||
else Shizuku.unbindUserService(userServiceArgs, userServiceConnection, true)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Accounts(
|
||||
val list: List<Account>
|
||||
) {
|
||||
@Serializable data class Account(val type: String, val name: String)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AccountsScreen(accounts: Accounts, onNavigateUp: () -> Unit) {
|
||||
MySmallTitleScaffold(R.string.accounts, onNavigateUp) {
|
||||
accounts.list.forEach {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth().padding(vertical = 4.dp)
|
||||
.clip(RoundedCornerShape(15)).background(MaterialTheme.colorScheme.surfaceVariant)
|
||||
) {
|
||||
SelectionContainer {
|
||||
Column(modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)) {
|
||||
Text(stringResource(R.string.type) + ": " + it.type)
|
||||
Text(stringResource(R.string.name) + ": " + it.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.system.Os
|
||||
import androidx.annotation.Keep
|
||||
import com.bintianqi.owndroid.IUserService
|
||||
import com.bintianqi.owndroid.getContext
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
@Keep
|
||||
class ShizukuService: IUserService.Stub() {
|
||||
override fun execute(command: String): String {
|
||||
var result = ""
|
||||
val process: Process
|
||||
try {
|
||||
process = Runtime.getRuntime().exec(command)
|
||||
val exitCode = process.waitFor()
|
||||
if(exitCode != 0) { result += "Error: $exitCode" }
|
||||
} catch(e: Exception) {
|
||||
e.printStackTrace()
|
||||
return e.toString()
|
||||
}
|
||||
try {
|
||||
val outputReader = process.inputStream.bufferedReader()
|
||||
var outputLine: String
|
||||
while(outputReader.readLine().also {outputLine = it} != null) { result += "$outputLine\n" }
|
||||
val errorReader = process.errorStream.bufferedReader()
|
||||
var errorLine: String
|
||||
while(errorReader.readLine().also {errorLine = it} != null) { result += "$errorLine\n" }
|
||||
} catch(e: NullPointerException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun getUid(): Int = Os.getuid()
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun listAccounts(): Array<Account> {
|
||||
val am = getContext().getSystemService(Context.ACCOUNT_SERVICE) as AccountManager
|
||||
return am.accounts
|
||||
}
|
||||
|
||||
override fun destroy() {
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,9 @@ import android.app.admin.DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED
|
||||
import android.app.admin.DevicePolicyManager.PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT
|
||||
import android.app.admin.DevicePolicyManager.WIPE_EUICC
|
||||
import android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE
|
||||
import android.content.*
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Binder
|
||||
import android.os.Build.VERSION
|
||||
import android.widget.Toast
|
||||
@@ -54,17 +56,16 @@ import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.bintianqi.owndroid.IUserService
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.myPrivilege
|
||||
import com.bintianqi.owndroid.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.CheckBoxItem
|
||||
import com.bintianqi.owndroid.ui.CopyTextButton
|
||||
import com.bintianqi.owndroid.ui.FunctionItem
|
||||
import com.bintianqi.owndroid.ui.InfoItem
|
||||
import com.bintianqi.owndroid.ui.MyScaffold
|
||||
import com.bintianqi.owndroid.ui.Notes
|
||||
import com.bintianqi.owndroid.ui.SwitchItem
|
||||
import com.bintianqi.owndroid.yesOrNo
|
||||
import com.bintianqi.owndroid.useShizuku
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable object WorkProfile
|
||||
@@ -73,7 +74,7 @@ import kotlinx.serialization.Serializable
|
||||
fun WorkProfileScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
||||
val privilege by myPrivilege.collectAsStateWithLifecycle()
|
||||
MyScaffold(R.string.work_profile, onNavigateUp, 0.dp) {
|
||||
if(VERSION.SDK_INT >= 30) {
|
||||
if(VERSION.SDK_INT >= 30 && !privilege.org) {
|
||||
FunctionItem(R.string.org_owned_work_profile, icon = R.drawable.corporate_fare_fill0) { onNavigate(OrganizationOwnedProfile) }
|
||||
}
|
||||
if(privilege.org) {
|
||||
@@ -162,22 +163,38 @@ fun CreateWorkProfileScreen(onNavigateUp: () -> Unit) {
|
||||
@Composable
|
||||
fun OrganizationOwnedProfileScreen(onNavigateUp: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
var dialog by remember { mutableStateOf(false) }
|
||||
MyScaffold(R.string.org_owned_work_profile, onNavigateUp) {
|
||||
InfoItem(R.string.org_owned_work_profile, dpm.isOrganizationOwnedDeviceWithManagedProfile.yesOrNo)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(!dpm.isOrganizationOwnedDeviceWithManagedProfile) {
|
||||
Button({
|
||||
useShizuku(context) { service ->
|
||||
val result = IUserService.Stub.asInterface(service).execute(activateOrgProfileCommand)
|
||||
if (result?.getInt("code", -1) == 0) {
|
||||
context.showOperationResultToast(true)
|
||||
} else {
|
||||
context.showOperationResultToast(false)
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Text(stringResource(R.string.shizuku))
|
||||
}
|
||||
Button({ dialog = true }) { Text(stringResource(R.string.adb_command)) }
|
||||
if(dialog) AlertDialog(
|
||||
text = {
|
||||
SelectionContainer {
|
||||
Text(
|
||||
text = stringResource(R.string.activate_org_profile_command, Binder.getCallingUid()/100000),
|
||||
color = colorScheme.onTertiaryContainer
|
||||
Text(activateOrgProfileCommand)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton({ dialog = false }) { Text(stringResource(R.string.confirm)) }
|
||||
},
|
||||
onDismissRequest = { dialog = false }
|
||||
)
|
||||
}
|
||||
CopyTextButton(R.string.copy_command, stringResource(R.string.activate_org_profile_command, Binder.getCallingUid()/100000))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val activateOrgProfileCommand = "dpm mark-profile-owner-on-organization-owned-device --user " +
|
||||
"${Binder.getCallingUid()/100000} com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver"
|
||||
|
||||
@Serializable object SuspendPersonalApp
|
||||
|
||||
@RequiresApi(30)
|
||||
|
||||
@@ -211,31 +211,6 @@ fun SwitchItem(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CopyTextButton(@StringRes label: Int, content: String) {
|
||||
val context = LocalContext.current
|
||||
var ok by remember{ mutableStateOf(false) }
|
||||
val scope = rememberCoroutineScope()
|
||||
Button(
|
||||
onClick = {
|
||||
if(!ok) {
|
||||
scope.launch {
|
||||
if(writeClipBoard(context,content)) { ok = true; delay(2000); ok = false }
|
||||
else{ Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show() }
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically, modifier = Modifier.animateContentSize()
|
||||
) {
|
||||
Icon(painter = painterResource(if(ok) R.drawable.check_fill0 else R.drawable.content_copy_fill0), contentDescription = null)
|
||||
Spacer(modifier = Modifier.padding(horizontal = 2.dp))
|
||||
Text(text = stringResource(if(ok) R.string.success else label))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InfoItem(title: Int, text: Int, withInfo: Boolean = false, onClick: () -> Unit = {}) =
|
||||
InfoItem(title, stringResource(text), withInfo, onClick)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<string name="success">Успешно</string>
|
||||
<string name="failure">Ошибка</string>
|
||||
<string name="failed">Ошибка</string>
|
||||
<string name="succeeded">Succeeded</string> <!--TODO-->
|
||||
<string name="add">Добавить</string>
|
||||
<string name="remove">Удалить</string>
|
||||
<string name="install">Установить</string>
|
||||
@@ -40,7 +41,6 @@
|
||||
<string name="decide_by_user">Определять пользователем</string>
|
||||
<string name="unsupported">Не поддерживается</string>
|
||||
<string name="options">Опции</string>
|
||||
<string name="copy_command">Копировать команду</string>
|
||||
<string name="package_name">Имя пакета</string>
|
||||
<string name="not_exist">Не существует</string>
|
||||
<string name="copy">Копировать</string>
|
||||
@@ -85,7 +85,6 @@
|
||||
<string name="change_package_state">Изменить состояние пакета</string>
|
||||
<string name="grant_permissions">Предоставить разрешения</string>
|
||||
<string name="add_delegated_admin">Добавить делегированного админа</string>
|
||||
<string name="dhizuku_will_be_deactivated">Dhizuku будет деактивирован</string>
|
||||
<string name="reset_device_policy">Сбросить политику устройства</string>
|
||||
<string name="device_info">Информация об устройстве</string>
|
||||
<string name="support_device_id_attestation">Поддержка аттестации идентификатора устройства</string>
|
||||
@@ -111,7 +110,6 @@
|
||||
<string name="long_support_msg">Подробное сообщение</string>
|
||||
<string name="transfer">Передать</string>
|
||||
<string name="transfer_ownership_warning">%1$s привилегия будет передана %2$s</string>
|
||||
<string name="activate_device_admin_here">Активируйте администратора устройства здесь.</string>
|
||||
|
||||
<!--Приемник-->
|
||||
<string name="create_work_profile_success">Рабочий профиль успешно создан</string>
|
||||
@@ -120,14 +118,6 @@
|
||||
<string name="failed_to_init_dhizuku">Не удалось инициализировать Dhizuku</string>
|
||||
<string name="dhizuku_permission_not_granted">Разрешение Dhizuku не предоставлено</string>
|
||||
<string name="dhizuku_mode_disabled">Режим Dhizuku отключен</string>
|
||||
<!--Shizuku-->
|
||||
<string name="list_owners">Список владельцев</string>
|
||||
<string name="list_users">Список пользователей</string>
|
||||
<string name="list_accounts">Список аккаунтов</string>
|
||||
<string name="shizuku_not_started">Shizuku не запущен. </string>
|
||||
<string name="activate_device_owner">Активировать владельца устройства</string>
|
||||
<string name="activate_org_profile">Активировать рабочий профиль, принадлежащий организации</string>
|
||||
<string name="accounts">Аккаунты</string>
|
||||
|
||||
<!--Системные-->
|
||||
<string name="system">Система</string>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<string name="success">Başarılı</string>
|
||||
<string name="failure">Başarısız</string>
|
||||
<string name="failed">Başarısız Oldu</string>
|
||||
<string name="succeeded">Succeeded</string> <!--TODO-->
|
||||
<string name="add">Ekle</string>
|
||||
<string name="remove">Kaldır</string>
|
||||
<string name="install">Yükle</string>
|
||||
@@ -40,7 +41,6 @@
|
||||
<string name="decide_by_user">Kullanıcı Tarafından Karar Ver</string>
|
||||
<string name="unsupported">Desteklenmiyor</string>
|
||||
<string name="options">Seçenekler</string>
|
||||
<string name="copy_command">Komutu Kopyala</string>
|
||||
<string name="package_name">Paket Adı</string>
|
||||
<string name="not_exist">Mevcut Değil</string>
|
||||
<string name="copy">Kopyala</string>
|
||||
@@ -84,7 +84,6 @@
|
||||
<string name="change_package_state">Paket Durumunu Değiştir</string>
|
||||
<string name="grant_permissions">İzinleri Ver</string>
|
||||
<string name="add_delegated_admin">Yetkilendirilmiş Yönetici Ekle</string>
|
||||
<string name="dhizuku_will_be_deactivated">Dhizuku Devre Dışı Bırakılacak</string>
|
||||
<string name="reset_device_policy">Cihaz Politikasını Sıfırla</string>
|
||||
<string name="device_info">Cihaz Bilgisi</string>
|
||||
<string name="support_device_id_attestation">Cihaz Kimliği Doğrulamasını Destekler</string>
|
||||
@@ -110,7 +109,6 @@
|
||||
<string name="long_support_msg">Uzun Mesaj</string>
|
||||
<string name="transfer">Aktar</string>
|
||||
<string name="transfer_ownership_warning">%1$s yetkisi %2$s\'e aktarılacak</string>
|
||||
<string name="activate_device_admin_here">Cihaz Yöneticisini Burada Etkinleştir.</string>
|
||||
|
||||
<!--Receiver-->
|
||||
<string name="create_work_profile_success">İş Profili Başarıyla Oluşturuldu</string>
|
||||
@@ -119,14 +117,6 @@
|
||||
<string name="failed_to_init_dhizuku">Dhizuku Başlatılamadı</string>
|
||||
<string name="dhizuku_permission_not_granted">Dhizuku İzni Verilmedi</string>
|
||||
<string name="dhizuku_mode_disabled">Dhizuku Modu Devre Dışı</string>
|
||||
<!--Shizuku-->
|
||||
<string name="list_owners">Sahip Listesi</string>
|
||||
<string name="list_users">Kullanıcı Listesi</string>
|
||||
<string name="list_accounts">Hesap Listesi</string>
|
||||
<string name="shizuku_not_started">Shizuku Başlatılmadı.</string>
|
||||
<string name="activate_device_owner">Cihaz Sahibini Etkinleştir</string>
|
||||
<string name="activate_org_profile">Kuruluşa Ait İş Profilini Etkinleştir</string>
|
||||
<string name="accounts">Hesaplar</string>
|
||||
|
||||
<!--System-->
|
||||
<string name="system">Sistem</string>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<string name="success">成功</string>
|
||||
<string name="failure">失败</string>
|
||||
<string name="failed">失败</string>
|
||||
<string name="succeeded">成功</string>
|
||||
<string name="add">添加</string>
|
||||
<string name="remove">移除</string>
|
||||
<string name="install">安装</string>
|
||||
@@ -40,7 +41,6 @@
|
||||
<string name="decide_by_user">由用户决定</string>
|
||||
<string name="unsupported">不支持</string>
|
||||
<string name="options">选项</string>
|
||||
<string name="copy_command">复制代码</string>
|
||||
<string name="copy">复制</string>
|
||||
<string name="file_not_exist">文件不存在</string>
|
||||
<string name="io_exception">IO异常</string>
|
||||
@@ -83,7 +83,6 @@
|
||||
<string name="grant_permissions">授予权限</string>
|
||||
<string name="add_delegated_admin">添加委托管理员</string>
|
||||
<string name="reset_device_policy">重置设备策略</string>
|
||||
<string name="dhizuku_will_be_deactivated">Dhizuku将被停用</string>
|
||||
<string name="device_info">设备信息</string>
|
||||
<string name="support_device_id_attestation">支持设备ID认证</string>
|
||||
<string name="support_unique_device_attestation">支持唯一设备认证</string>
|
||||
@@ -107,7 +106,6 @@
|
||||
<string name="long_support_msg">提供支持的长消息</string>
|
||||
<string name="transfer">转移</string>
|
||||
<string name="transfer_ownership_warning">%1$s 特权将被转移至 %2$s</string>
|
||||
<string name="activate_device_admin_here">在这里激活Device admin</string>
|
||||
|
||||
<!--Receiver-->
|
||||
<string name="create_work_profile_success">创建工作资料成功</string>
|
||||
@@ -117,15 +115,6 @@
|
||||
<string name="dhizuku_permission_not_granted">Dhizuku未授权</string>
|
||||
<string name="dhizuku_mode_disabled">Dhizuku模式已禁用</string>
|
||||
|
||||
<!--Shizuku-->
|
||||
<string name="list_owners">列出Owners</string>
|
||||
<string name="list_users">列出用户</string>
|
||||
<string name="list_accounts">列出账号</string>
|
||||
<string name="shizuku_not_started">服务未启动</string>
|
||||
<string name="activate_device_owner">激活Device owner</string>
|
||||
<string name="activate_org_profile">激活由组织拥有的工作资料</string>
|
||||
<string name="accounts">账号</string>
|
||||
|
||||
<!--System-->
|
||||
<string name="system">系统</string>
|
||||
<string name="disable_cam">禁用相机</string>
|
||||
@@ -555,7 +544,7 @@
|
||||
<!--Settings&About-->
|
||||
<string name="settings">设置</string>
|
||||
<string name="show_dangerous_features">显示危险功能</string>
|
||||
<string name="material_you_color">Material you 颜色</string>
|
||||
<string name="material_you_color">Material You 颜色</string>
|
||||
<string name="dark_theme">深色主题</string>
|
||||
<string name="follow_system">跟随系统</string>
|
||||
<string name="black_theme">黑色主题</string>
|
||||
@@ -695,4 +684,11 @@
|
||||
<string name="info_max_failed_password_other_user">如果设置为大于零的值,将会在输入一定次数的错误的锁屏密码后清除当前用户的所有数据</string>
|
||||
<string name="info_password_history_length">设置后,用户将无法输入与历史记录中任何密码相同的新密码。当前密码将保留,直到用户设置新密码为止,因此更改不会立即生效。值为0表示不做限制。</string>
|
||||
<string name="info_required_strong_auth_timeout">如果用户在这段时间内没有使用强认证(密码、PIN或图案)解锁设备,则要求使用强认证解锁设备。值为0表示OwnDroid不参与控制超时。一般来说,最少1小时,最多72小时。</string>
|
||||
<string name="info_deactivate">OwnDroid will lost its privilege</string>
|
||||
|
||||
<string name="choose_work_mode">选择一个工作模式</string>
|
||||
<string name="recommended">推荐</string>
|
||||
<string name="activate_method">激活方法</string>
|
||||
<string name="adb_command">ADB命令</string>
|
||||
<string name="owndroid_warning">此应用使用Device owner和Profile owner特权。这些特权十分危险,请谨慎使用。如果操作不当,可能会造成严重损失。开发者将不会对此负责。</string>
|
||||
</resources>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<string name="enable">Enable</string>
|
||||
<string name="success">Success</string>
|
||||
<string name="failure">Failure</string>
|
||||
<string name="succeeded">Succeeded</string>
|
||||
<string name="failed">Failed</string>
|
||||
<string name="add">Add</string>
|
||||
<string name="remove">Remove</string>
|
||||
@@ -42,7 +43,6 @@
|
||||
<string name="decide_by_user">Decide by user</string>
|
||||
<string name="unsupported">Unsupported</string>
|
||||
<string name="options">Options</string>
|
||||
<string name="copy_command">Copy Command</string>
|
||||
<string name="package_name">Package name</string>
|
||||
<string name="not_exist">Not exist</string>
|
||||
<string name="copy">Copy</string>
|
||||
@@ -87,10 +87,7 @@
|
||||
<string name="change_package_state">Change package state</string>
|
||||
<string name="grant_permissions">Grant permissions</string>
|
||||
<string name="add_delegated_admin">Add delegated admin</string>
|
||||
<string name="dhizuku_will_be_deactivated">Dhizuku will be deactivated</string>
|
||||
<string name="reset_device_policy">Reset device policy</string>
|
||||
<string name="activate_profile_owner_command" translatable="false">dpm set-profile-owner --user %1$s com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver</string>
|
||||
<string name="activate_device_owner_command" translatable="false">dpm set-device-owner com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver</string>
|
||||
<string name="device_info">Device info</string>
|
||||
<string name="support_device_id_attestation">Support Device ID attestation</string>
|
||||
<string name="support_unique_device_attestation">Support unique device attestation</string>
|
||||
@@ -115,7 +112,6 @@
|
||||
<string name="long_support_msg">Long message</string>
|
||||
<string name="transfer">Transfer</string>
|
||||
<string name="transfer_ownership_warning">%1$s privilege will be transferred to %2$s</string>
|
||||
<string name="activate_device_admin_here">Activate Device admin here.</string>
|
||||
|
||||
<!--Receiver-->
|
||||
<string name="create_work_profile_success">Create work profile success</string>
|
||||
@@ -125,16 +121,8 @@
|
||||
<string name="failed_to_init_dhizuku">Failed to initialize Dhizuku</string>
|
||||
<string name="dhizuku_permission_not_granted">Dhizuku permission not granted</string>
|
||||
<string name="dhizuku_mode_disabled">Dhizuku mode disabled</string>
|
||||
<!--Shizuku-->
|
||||
|
||||
<string name="shizuku" translatable="false">Shizuku</string>
|
||||
<string name="list_owners">List owners</string>
|
||||
<string name="list_users">List users</string>
|
||||
<string name="list_accounts">List accounts</string>
|
||||
<string name="shizuku_not_started">Shizuku not started. </string>
|
||||
<string name="dpm_activate_do_command" translatable="false">dpm set-device-owner com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver</string>
|
||||
<string name="activate_device_owner">Activate Device owner</string>
|
||||
<string name="activate_org_profile">Activate organization-owned work profile</string>
|
||||
<string name="accounts">Accounts</string>
|
||||
|
||||
<!--System-->
|
||||
<string name="system">System</string>
|
||||
@@ -371,9 +359,6 @@
|
||||
<string name="account_name">Account name</string>
|
||||
<string name="keep_account">Keep account</string>
|
||||
<string name="org_owned_work_profile">Organization work profile</string>
|
||||
<string name="activate_org_profile_command" tools:ignore="TypographyDashes" translatable="false">
|
||||
dpm mark-profile-owner-on-organization-owned-device --user %1$s com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver
|
||||
</string>
|
||||
<string name="skip_encryption">Skip encryption</string>
|
||||
<string name="create">Create</string>
|
||||
<string name="suspend_personal_app">Suspend personal app</string>
|
||||
@@ -592,7 +577,7 @@
|
||||
<!--Settings&About-->
|
||||
<string name="settings">Settings</string>
|
||||
<string name="show_dangerous_features">Show dangerous features</string>
|
||||
<string name="material_you_color">Material you color</string>
|
||||
<string name="material_you_color">Material You color</string>
|
||||
<string name="dark_theme">Dark theme</string>
|
||||
<string name="follow_system">Follow system</string>
|
||||
<string name="black_theme">Black theme</string>
|
||||
@@ -733,4 +718,11 @@
|
||||
<string name="info_max_failed_password_other_user">Setting this to a value greater than zero enables a policy that will wipe this user after too many incorrect unlock password have been entered.</string>
|
||||
<string name="info_password_history_length">After setting this, the user will not be able to enter a new password that is the same as any password in the history. Note that the current password will remain until the user has set a new one, so the change does not take place immediately.\nA value of 0 means there is no restriction.</string>
|
||||
<string name="info_required_strong_auth_timeout">Determine for how long the user will be able to use secondary, non strong auth for authentication, since last strong method authentication (password, pin or pattern) was used. After the returned timeout the user is required to use strong authentication method.\nA value of 0 means the admin is not participating in controlling the timeout. The minimum and maximum timeouts are platform-defined and are typically 1 hour and 72 hours, respectively.</string>
|
||||
<string name="info_deactivate">OwnDroid will lost its privilege</string>
|
||||
|
||||
<string name="choose_work_mode">Choose a work mode</string>
|
||||
<string name="recommended">Recommended</string>
|
||||
<string name="activate_method">Activate method</string>
|
||||
<string name="adb_command">ADB command</string>
|
||||
<string name="owndroid_warning">This app uses Device owner and Profile owner privileges. These privileges are extremely dangerous, please use them with caution. If used improperly, they may result in severe losses. The developers will not be responsible for this.</string>
|
||||
</resources>
|
||||
|
||||
@@ -3,13 +3,14 @@ agp = "8.9.1"
|
||||
kotlin = "2.1.20"
|
||||
|
||||
navigation-compose = "2.8.9"
|
||||
composeBom = "2025.03.01"
|
||||
composeBom = "2025.04.00"
|
||||
accompanist-drawablepainter = "0.35.0-alpha"
|
||||
accompanist-permissions = "0.37.0"
|
||||
shizuku = "13.1.5"
|
||||
fragment = "1.8.6"
|
||||
dhizuku = "2.5.3"
|
||||
hiddenApiBypass = "4.3"
|
||||
libsu = "6.0.0"
|
||||
serialization = "1.7.3"
|
||||
|
||||
[libraries]
|
||||
@@ -19,6 +20,7 @@ androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-toolin
|
||||
androidx-activity-compose = { module = "androidx.activity:activity-compose" }
|
||||
androidx-material3 = { module = "androidx.compose.material3:material3" }
|
||||
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation-compose" }
|
||||
androidx-fragment = { group = "androidx.fragment", name = "fragment", version.ref = "fragment" }
|
||||
|
||||
accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version.ref = "accompanist-drawablepainter" }
|
||||
accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanist-permissions" }
|
||||
@@ -27,7 +29,7 @@ shizuku-provider = { module = "dev.rikka.shizuku:provider", version.ref = "shizu
|
||||
shizuku-api = { module = "dev.rikka.shizuku:api", version.ref = "shizuku" }
|
||||
dhizuku-api = { module = "io.github.iamr0s:Dhizuku-API", version.ref = "dhizuku" }
|
||||
hiddenApiBypass = { module = "org.lsposed.hiddenapibypass:hiddenapibypass", version.ref = "hiddenApiBypass" }
|
||||
androidx-fragment = { group = "androidx.fragment", name = "fragment", version.ref = "fragment" }
|
||||
libsu = { module = "com.github.topjohnwu.libsu:core", version.ref = "libsu" }
|
||||
|
||||
serialization = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" }
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ dependencyResolutionManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven("https://jitpack.io")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user