Add Privilege class

This commit is contained in:
BinTianqi
2025-04-06 15:32:11 +08:00
parent 576507a78d
commit 6c92c7dcbe
23 changed files with 339 additions and 481 deletions

View File

@@ -3,7 +3,7 @@ name: Build APK
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: ["master"] branches: ["dev"]
paths-ignore: paths-ignore:
- '**.md' - '**.md'
tags-ignore: tags-ignore:
@@ -24,7 +24,7 @@ jobs:
- name: Set up JDK 21 - name: Set up JDK 21
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
distribution: 'adopt' distribution: 'temurin'
java-version: '21' java-version: '21'
- name: Get short commit SHA - name: Get short commit SHA

View File

@@ -80,6 +80,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.net.URLDecoder import java.net.URLDecoder
import androidx.core.net.toUri
class AppInstallerActivity:FragmentActivity() { class AppInstallerActivity:FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -116,11 +117,11 @@ private fun AppInstaller(
installing: Boolean = false, installing: Boolean = false,
options: SessionParamsOptions = SessionParamsOptions(), options: SessionParamsOptions = SessionParamsOptions(),
onOptionsChange: (SessionParamsOptions) -> Unit = {}, onOptionsChange: (SessionParamsOptions) -> Unit = {},
packages: Set<Uri> = setOf(Uri.parse("https://example.com")), packages: Set<Uri> = setOf("https://example.com".toUri()),
onPackageRemove: (Uri) -> Unit = {}, onPackageRemove: (Uri) -> Unit = {},
onPackageChoose: (List<Uri>) -> Unit = {}, onPackageChoose: (List<Uri>) -> Unit = {},
onStartInstall: () -> Unit = {}, onStartInstall: () -> Unit = {},
writtenPackages: Set<Uri> = setOf(Uri.parse("https://example.com")), writtenPackages: Set<Uri> = setOf("https://example.com".toUri()),
writingPackage: Uri? = null, writingPackage: Uri? = null,
result: Intent? = null, result: Intent? = null,
onResultDialogClose: () -> Unit = {} onResultDialogClose: () -> Unit = {}
@@ -309,7 +310,7 @@ class AppInstallerViewModel(application: Application): AndroidViewModel(applicat
intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)?.let { uri -> packages.update { it + uri } } intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)?.let { uri -> packages.update { it + uri } }
intent.getParcelableArrayExtra(Intent.EXTRA_STREAM)?.forEach { uri -> packages.update { it + (uri as Uri) } } intent.getParcelableArrayExtra(Intent.EXTRA_STREAM)?.forEach { uri -> packages.update { it + (uri as Uri) } }
intent.clipData?.let { clipData -> intent.clipData?.let { clipData ->
for(i in 0..(clipData.itemCount - 1)) { for(i in 0..clipData.itemCount) {
packages.update { it + clipData.getItemAt(i).uri } packages.update { it + clipData.getItemAt(i).uri }
} }
} }

View File

@@ -35,9 +35,6 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
@@ -108,8 +105,6 @@ import com.bintianqi.owndroid.dpm.DelegatedAdmins
import com.bintianqi.owndroid.dpm.DelegatedAdminsScreen import com.bintianqi.owndroid.dpm.DelegatedAdminsScreen
import com.bintianqi.owndroid.dpm.DeleteWorkProfile import com.bintianqi.owndroid.dpm.DeleteWorkProfile
import com.bintianqi.owndroid.dpm.DeleteWorkProfileScreen import com.bintianqi.owndroid.dpm.DeleteWorkProfileScreen
import com.bintianqi.owndroid.dpm.DeviceAdmin
import com.bintianqi.owndroid.dpm.DeviceAdminScreen
import com.bintianqi.owndroid.dpm.DeviceInfo import com.bintianqi.owndroid.dpm.DeviceInfo
import com.bintianqi.owndroid.dpm.DeviceInfoScreen import com.bintianqi.owndroid.dpm.DeviceInfoScreen
import com.bintianqi.owndroid.dpm.DeviceOwner import com.bintianqi.owndroid.dpm.DeviceOwner
@@ -241,9 +236,6 @@ import com.bintianqi.owndroid.dpm.dhizukuErrorStatus
import com.bintianqi.owndroid.dpm.dhizukuPermissionGranted import com.bintianqi.owndroid.dpm.dhizukuPermissionGranted
import com.bintianqi.owndroid.dpm.getDPM import com.bintianqi.owndroid.dpm.getDPM
import com.bintianqi.owndroid.dpm.getReceiver import com.bintianqi.owndroid.dpm.getReceiver
import com.bintianqi.owndroid.dpm.isDeviceAdmin
import com.bintianqi.owndroid.dpm.isDeviceOwner
import com.bintianqi.owndroid.dpm.isProfileOwner
import com.bintianqi.owndroid.dpm.setDefaultAffiliationID import com.bintianqi.owndroid.dpm.setDefaultAffiliationID
import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
@@ -259,7 +251,6 @@ val backToHomeStateFlow = MutableStateFlow(false)
@ExperimentalMaterial3Api @ExperimentalMaterial3Api
class MainActivity : FragmentActivity() { class MainActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
registerActivityResult(this)
enableEdgeToEdge() enableEdgeToEdge()
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -288,6 +279,7 @@ class MainActivity : FragmentActivity() {
dhizukuErrorStatus.value = 1 dhizukuErrorStatus.value = 1
} }
} }
updatePrivilege(this)
} }
} }
@@ -325,9 +317,8 @@ fun Home(vm: MyViewModel) {
composable<Permissions> { composable<Permissions> {
PermissionsScreen(::navigateUp, { navController.navigate(it) }) { navController.navigate(ShizukuScreen, it) } PermissionsScreen(::navigateUp, { navController.navigate(it) }) { navController.navigate(ShizukuScreen, it) }
} }
composable<ShizukuScreen> { ShizukuScreen(it.arguments!!, ::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<Accounts>(mapOf(serializableNavTypePair<List<Accounts.Account>>())) { AccountsScreen(it.toRoute(), ::navigateUp) }
composable<DeviceAdmin> { DeviceAdminScreen(::navigateUp) }
composable<ProfileOwner> { ProfileOwnerScreen(::navigateUp) } composable<ProfileOwner> { ProfileOwnerScreen(::navigateUp) }
composable<DeviceOwner> { DeviceOwnerScreen(::navigateUp) } composable<DeviceOwner> { DeviceOwnerScreen(::navigateUp) }
composable<DelegatedAdmins> { DelegatedAdminsScreen(::navigateUp, ::navigate) } composable<DelegatedAdmins> { DelegatedAdminsScreen(::navigateUp, ::navigate) }
@@ -385,8 +376,8 @@ fun Home(vm: MyViewModel) {
composable<DeleteWorkProfile> { DeleteWorkProfileScreen(::navigateUp) } composable<DeleteWorkProfile> { DeleteWorkProfileScreen(::navigateUp) }
composable<ApplicationsList> { composable<ApplicationsList> {
AppChooserScreen(it.toRoute(), { AppChooserScreen(it.toRoute(), { dest ->
if(it == null) navigateUp() else navigate(ApplicationDetails(it)) if(dest == null) navigateUp() else navigate(ApplicationDetails(dest))
}, { }, {
SharedPrefs(context).applicationsListView = false SharedPrefs(context).applicationsListView = false
navController.navigate(ApplicationsFeatures) { navController.navigate(ApplicationsFeatures) {
@@ -489,7 +480,7 @@ fun Home(vm: MyViewModel) {
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
val dpm = context.getDPM() val dpm = context.getDPM()
val sp = SharedPrefs(context) val sp = SharedPrefs(context)
val profileNotActivated = !sp.managedProfileActivated && context.isProfileOwner && (VERSION.SDK_INT < 24 || dpm.isManagedProfile(receiver)) val profileNotActivated = !sp.managedProfileActivated && myPrivilege.value.work
if(profileNotActivated) { if(profileNotActivated) {
dpm.setProfileEnabled(receiver) dpm.setProfileEnabled(receiver)
sp.managedProfileActivated = true sp.managedProfileActivated = true
@@ -505,24 +496,14 @@ fun Home(vm: MyViewModel) {
private fun HomeScreen(onNavigate: (Any) -> Unit) { private fun HomeScreen(onNavigate: (Any) -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val privilege by myPrivilege.collectAsStateWithLifecycle()
var activated by remember { mutableStateOf(false) } val activateType = (if(privilege.dhizuku) context.getString(R.string.dhizuku) + " - " else "") +
var activateType by remember { mutableStateOf("") } context.getString(
val deviceAdmin = context.isDeviceAdmin if(privilege.device) R.string.device_owner
val deviceOwner = context.isDeviceOwner else if(privilege.work) R.string.work_profile_owner
val profileOwner = context.isProfileOwner else if(privilege.profile) R.string.profile_owner
val refreshStatus by dhizukuErrorStatus.collectAsState() else R.string.click_to_activate
LaunchedEffect(refreshStatus) {
activated = context.isProfileOwner || context.isDeviceOwner
activateType = if(SharedPrefs(context).dhizuku) context.getString(R.string.dhizuku) + " - " else ""
activateType += context.getString(
if(deviceOwner) { R.string.device_owner }
else if(profileOwner) {
if(VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)) R.string.work_profile_owner else R.string.profile_owner
}
else if(deviceAdmin) R.string.device_admin else R.string.click_to_activate
) )
}
Scaffold { Scaffold {
Column(modifier = Modifier.padding(it).verticalScroll(rememberScrollState())) { Column(modifier = Modifier.padding(it).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 25.dp)) Spacer(Modifier.padding(vertical = 25.dp))
@@ -541,6 +522,7 @@ private fun HomeScreen(onNavigate: (Any) -> Unit) {
.padding(vertical = 16.dp), .padding(vertical = 16.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
val activated = privilege.device || privilege.profile
Icon( Icon(
painterResource(if(activated) R.drawable.check_circle_fill1 else R.drawable.block_fill0), null, painterResource(if(activated) R.drawable.check_circle_fill1 else R.drawable.block_fill0), null,
Modifier.padding(start = 14.dp), colorScheme.onPrimary Modifier.padding(start = 14.dp), colorScheme.onPrimary
@@ -555,23 +537,32 @@ private fun HomeScreen(onNavigate: (Any) -> Unit) {
if(activateType != "") { Text(text = activateType, color = colorScheme.onPrimary) } if(activateType != "") { Text(text = activateType, color = colorScheme.onPrimary) }
} }
} }
if(privilege.device || privilege.profile) {
HomePageItem(R.string.system, R.drawable.android_fill0) { onNavigate(SystemManager) } HomePageItem(R.string.system, R.drawable.android_fill0) { onNavigate(SystemManager) }
if(deviceOwner || profileOwner) { HomePageItem(R.string.network, R.drawable.wifi_fill0) { onNavigate(Network) } } HomePageItem(R.string.network, R.drawable.wifi_fill0) { onNavigate(Network) }
if(
(VERSION.SDK_INT < 24 && !deviceOwner) || (dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE) ||
(profileOwner && dpm.isManagedProfile(receiver))
)
) {
HomePageItem(R.string.work_profile, R.drawable.work_fill0) { onNavigate(WorkProfile) }
} }
if(deviceOwner || profileOwner) HomePageItem(R.string.applications, R.drawable.apps_fill0) { if(
privilege.work || (VERSION.SDK_INT < 24 && !privilege.device) ||
dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)
) {
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
)
}
}
if(privilege.device || privilege.profile) {
HomePageItem(R.string.applications, R.drawable.apps_fill0) {
onNavigate(if(SharedPrefs(context).applicationsListView) ApplicationsList(true) else ApplicationsFeatures) onNavigate(if(SharedPrefs(context).applicationsListView) ApplicationsList(true) else ApplicationsFeatures)
} }
if(VERSION.SDK_INT >= 24 && (profileOwner || deviceOwner)) { if(VERSION.SDK_INT >= 24) {
HomePageItem(R.string.user_restriction, R.drawable.person_off) { onNavigate(UserRestriction) } HomePageItem(R.string.user_restriction, R.drawable.person_off) { onNavigate(UserRestriction) }
} }
HomePageItem(R.string.users,R.drawable.manage_accounts_fill0) { onNavigate(Users) } HomePageItem(R.string.users,R.drawable.manage_accounts_fill0) { onNavigate(Users) }
if(deviceOwner || profileOwner) HomePageItem(R.string.password_and_keyguard, R.drawable.password_fill0) { onNavigate(Password) } HomePageItem(R.string.password_and_keyguard, R.drawable.password_fill0) { onNavigate(Password) }
}
HomePageItem(R.string.settings, R.drawable.settings_fill0) { onNavigate(Settings) } HomePageItem(R.string.settings, R.drawable.settings_fill0) { onNavigate(Settings) }
Spacer(Modifier.padding(vertical = 20.dp)) Spacer(Modifier.padding(vertical = 20.dp))
} }

View File

@@ -18,6 +18,7 @@ import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
import kotlin.system.exitProcess import kotlin.system.exitProcess
import androidx.core.content.edit
class ManageSpaceActivity: FragmentActivity() { class ManageSpaceActivity: FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -62,7 +63,7 @@ class ManageSpaceActivity: FragmentActivity() {
dataDir.resolve("shared_prefs").deleteRecursively() dataDir.resolve("shared_prefs").deleteRecursively()
} else { } else {
val sharedPref = applicationContext.getSharedPreferences("data", MODE_PRIVATE) val sharedPref = applicationContext.getSharedPreferences("data", MODE_PRIVATE)
sharedPref.edit().clear().apply() sharedPref.edit { clear() }
} }
this.showOperationResultToast(true) this.showOperationResultToast(true)
finish() finish()

View File

@@ -0,0 +1,39 @@
package com.bintianqi.owndroid
import android.content.Context
import android.os.Binder
import android.os.Build
import com.bintianqi.owndroid.dpm.getDPM
import com.bintianqi.owndroid.dpm.getReceiver
import com.bintianqi.owndroid.dpm.isDeviceOwner
import com.bintianqi.owndroid.dpm.isProfileOwner
import kotlinx.coroutines.flow.MutableStateFlow
class Privilege(
val device: Boolean = false, // Device owner
val profile: Boolean = false, // Profile owner
val dhizuku: Boolean = false,
val work: Boolean = false, // Work profile
val org: Boolean = false, // Organization-owned work profile
val affiliated: Boolean = false
) {
val primary = Binder.getCallingUid() / 100000 == 0 // Primary user
}
val myPrivilege = MutableStateFlow(Privilege())
fun updatePrivilege(context: Context) {
val dpm = context.getDPM()
val receiver = context.getReceiver()
val profile = context.isProfileOwner
val work = profile && Build.VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)
myPrivilege.value = Privilege(
device = context.isDeviceOwner,
profile = profile,
dhizuku = SharedPrefs(context).dhizuku,
work = work,
org = work && Build.VERSION.SDK_INT >= 30 && dpm.isOrganizationOwnedDeviceWithManagedProfile,
affiliated = Build.VERSION.SDK_INT >= 28 && dpm.isAffiliatedUser
)
}

View File

@@ -6,21 +6,15 @@ import android.app.admin.DeviceAdminReceiver
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import android.graphics.drawable.Icon
import android.os.Build.VERSION import android.os.Build.VERSION
import android.os.PersistableBundle import android.os.PersistableBundle
import android.os.UserHandle import android.os.UserHandle
import android.os.UserManager import android.os.UserManager
import android.widget.Toast import android.widget.Toast
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.graphics.drawable.toBitmap
import com.bintianqi.owndroid.dpm.handleNetworkLogs import com.bintianqi.owndroid.dpm.handleNetworkLogs
import com.bintianqi.owndroid.dpm.isDeviceOwner import com.bintianqi.owndroid.dpm.handlePrivilegeChange
import com.bintianqi.owndroid.dpm.isProfileOwner
import com.bintianqi.owndroid.dpm.processSecurityLogs import com.bintianqi.owndroid.dpm.processSecurityLogs
import com.bintianqi.owndroid.dpm.setDefaultAffiliationID
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -38,35 +32,18 @@ class Receiver : DeviceAdminReceiver() {
dpm.setLockTaskPackages(receiver, arrayOf()) dpm.setLockTaskPackages(receiver, arrayOf())
dpm.setLockTaskPackages(receiver, packages) dpm.setLockTaskPackages(receiver, packages)
} }
if(!context.isDeviceOwner && !context.isProfileOwner) SharedPrefs(context).isApiEnabled = false
} }
override fun onEnabled(context: Context, intent: Intent) { override fun onEnabled(context: Context, intent: Intent) {
super.onEnabled(context, intent) super.onEnabled(context, intent)
if(context.isProfileOwner || context.isDeviceOwner){ updatePrivilege(context)
setDefaultAffiliationID(context) handlePrivilegeChange(context)
Toast.makeText(context, context.getString(R.string.onEnabled), Toast.LENGTH_SHORT).show()
if(VERSION.SDK_INT >= 25) {
val sm = context.getSystemService(ShortcutManager::class.java)
val lockIntent = Intent("com.bintianqi.owndroid.action.LOCK")
.setComponent(ComponentName(context, ShortcutsReceiverActivity::class.java))
val shortcut = ShortcutInfo.Builder(context, "LockScreen")
.setShortLabel(context.getString(R.string.lock_now))
.setIcon(Icon.createWithBitmap(context.getDrawable(R.drawable.screen_lock_portrait_fill0)?.toBitmap()))
.setIntent(lockIntent)
sm.addDynamicShortcuts(listOf(shortcut.build()))
}
}
} }
override fun onDisabled(context: Context, intent: Intent) { override fun onDisabled(context: Context, intent: Intent) {
super.onDisabled(context, intent) super.onDisabled(context, intent)
Toast.makeText(context, R.string.onDisabled, Toast.LENGTH_SHORT).show() updatePrivilege(context)
SharedPrefs(context).isDefaultAffiliationIdSet = false handlePrivilegeChange(context)
if(VERSION.SDK_INT >= 25) {
val sm = context.getSystemService(ShortcutManager::class.java)
sm.removeDynamicShortcuts(listOf("LockScreen"))
}
} }
override fun onProfileProvisioningComplete(context: Context, intent: Intent) { override fun onProfileProvisioningComplete(context: Context, intent: Intent) {

View File

@@ -2,7 +2,6 @@ package com.bintianqi.owndroid
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Build.VERSION import android.os.Build.VERSION
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
@@ -45,6 +44,7 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.edit import androidx.core.content.edit
import androidx.core.net.toUri
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.Notes import com.bintianqi.owndroid.ui.Notes
@@ -285,7 +285,7 @@ fun AboutScreen(onNavigateUp: () -> Unit) {
} }
fun shareLink(inputContext: Context, link: String) { fun shareLink(inputContext: Context, link: String) {
val uri = Uri.parse(link) val uri = link.toUri()
val intent = Intent(Intent.ACTION_VIEW, uri) val intent = Intent(Intent.ACTION_VIEW, uri)
inputContext.startActivity(Intent.createChooser(intent, "Open in browser"), null) inputContext.startActivity(Intent.createChooser(intent, "Open in browser"), null)
} }

View File

@@ -1,13 +1,14 @@
package com.bintianqi.owndroid package com.bintianqi.owndroid
import android.content.Context import android.content.Context
import android.content.SharedPreferences
import android.os.Build import android.os.Build
import androidx.core.content.edit import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
class SharedPrefs(context: Context) { class SharedPrefs(context: Context) {
val sharedPrefs = context.getSharedPreferences("data", Context.MODE_PRIVATE) val sharedPrefs: SharedPreferences = context.getSharedPreferences("data", Context.MODE_PRIVATE)
var managedProfileActivated by BooleanSharedPref("managed_profile_activated") var managedProfileActivated by BooleanSharedPref("managed_profile_activated")
var dhizuku by BooleanSharedPref("dhizuku_mode") var dhizuku by BooleanSharedPref("dhizuku_mode")
var isDefaultAffiliationIdSet by BooleanSharedPref("default_affiliation_id_set") var isDefaultAffiliationIdSet by BooleanSharedPref("default_affiliation_id_set")

View File

@@ -1,25 +1,20 @@
package com.bintianqi.owndroid package com.bintianqi.owndroid
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.admin.DevicePolicyManager
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContract import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.NavType import androidx.navigation.NavType
import com.bintianqi.owndroid.dpm.addDeviceAdmin
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.io.FileNotFoundException import java.io.FileNotFoundException
@@ -60,15 +55,6 @@ fun writeClipBoard(context: Context, string: String):Boolean{
return true return true
} }
fun registerActivityResult(context: ComponentActivity) {
addDeviceAdmin = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val dpm = context.applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
if(dpm.isAdminActive(ComponentName(context.applicationContext, Receiver::class.java))) {
backToHomeStateFlow.value = true
}
}
}
fun formatFileSize(bytes: Long): String { fun formatFileSize(bytes: Long): String {
val kb = 1024 val kb = 1024
val mb = kb * 1024 val mb = kb * 1024
@@ -91,15 +77,11 @@ fun parseTimestamp(timestamp: Long): String {
return formatter.format(instant) return formatter.format(instant)
} }
fun parseDate(date: Date) fun parseDate(date: Date): String = SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()).format(date)
= SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()).format(date)
val Long.humanReadableDate: String val Long.humanReadableDate: String
get() = SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(Date(this)) get() = SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(Date(this))
val Long.humanReadableDateTime: String
get() = SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()).format(Date(this))
fun formatDate(pattern: String, value: Long): String fun formatDate(pattern: String, value: Long): String
= SimpleDateFormat(pattern, Locale.getDefault()).format(Date(value)) = SimpleDateFormat(pattern, Locale.getDefault()).format(Date(value))

View File

@@ -76,6 +76,7 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.AppInfo import com.bintianqi.owndroid.AppInfo
import com.bintianqi.owndroid.AppInstallerActivity import com.bintianqi.owndroid.AppInstallerActivity
import com.bintianqi.owndroid.AppInstallerViewModel import com.bintianqi.owndroid.AppInstallerViewModel
@@ -84,6 +85,7 @@ import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.getInstalledAppsFlags import com.bintianqi.owndroid.getInstalledAppsFlags
import com.bintianqi.owndroid.installedApps import com.bintianqi.owndroid.installedApps
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.ErrorDialog import com.bintianqi.owndroid.ui.ErrorDialog
import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem
@@ -179,14 +181,11 @@ fun ApplicationsFeaturesScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Un
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(bottom = 80.dp) .padding(bottom = 80.dp)
) { ) {
val dpm = context.getDPM() val privilege by myPrivilege.collectAsStateWithLifecycle()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
if(VERSION.SDK_INT >= 24) FunctionItem(R.string.suspend, icon = R.drawable.block_fill0) { onNavigate(Suspend) } if(VERSION.SDK_INT >= 24) FunctionItem(R.string.suspend, icon = R.drawable.block_fill0) { onNavigate(Suspend) }
FunctionItem(R.string.hide, icon = R.drawable.visibility_off_fill0) { onNavigate(Hide) } FunctionItem(R.string.hide, icon = R.drawable.visibility_off_fill0) { onNavigate(Hide) }
FunctionItem(R.string.block_uninstall, icon = R.drawable.delete_forever_fill0) { onNavigate(BlockUninstall) } FunctionItem(R.string.block_uninstall, icon = R.drawable.delete_forever_fill0) { onNavigate(BlockUninstall) }
if(VERSION.SDK_INT >= 30 && (deviceOwner || (VERSION.SDK_INT >= 33 && profileOwner))) { if(VERSION.SDK_INT >= 30 && (privilege.device || (VERSION.SDK_INT >= 33 && privilege.profile))) {
FunctionItem(R.string.disable_user_control, icon = R.drawable.do_not_touch_fill0) { onNavigate(DisableUserControl) } FunctionItem(R.string.disable_user_control, icon = R.drawable.do_not_touch_fill0) { onNavigate(DisableUserControl) }
} }
if(VERSION.SDK_INT >= 23) { if(VERSION.SDK_INT >= 23) {
@@ -202,19 +201,19 @@ fun ApplicationsFeaturesScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Un
context.startActivity(Intent(context, AppInstallerActivity::class.java)) context.startActivity(Intent(context, AppInstallerActivity::class.java))
} }
FunctionItem(R.string.uninstall_app, icon = R.drawable.delete_fill0) { onNavigate(UninstallApp) } FunctionItem(R.string.uninstall_app, icon = R.drawable.delete_fill0) { onNavigate(UninstallApp) }
if(VERSION.SDK_INT >= 28 && deviceOwner) { if(VERSION.SDK_INT >= 28 && privilege.device) {
FunctionItem(R.string.keep_uninstalled_packages, icon = R.drawable.delete_fill0) { onNavigate(KeepUninstalledPackages) } FunctionItem(R.string.keep_uninstalled_packages, icon = R.drawable.delete_fill0) { onNavigate(KeepUninstalledPackages) }
} }
if(VERSION.SDK_INT >= 28) FunctionItem(R.string.install_existing_app, icon = R.drawable.install_mobile_fill0) { if(VERSION.SDK_INT >= 28) FunctionItem(R.string.install_existing_app, icon = R.drawable.install_mobile_fill0) {
onNavigate(InstallExistingApp) onNavigate(InstallExistingApp)
} }
if(VERSION.SDK_INT >= 30 && profileOwner && dpm.isManagedProfile(receiver)) { if(VERSION.SDK_INT >= 30 && privilege.work) {
FunctionItem(R.string.cross_profile_apps, icon = R.drawable.work_fill0) { onNavigate(CrossProfilePackages) } FunctionItem(R.string.cross_profile_apps, icon = R.drawable.work_fill0) { onNavigate(CrossProfilePackages) }
} }
if(profileOwner) { if(privilege.work) {
FunctionItem(R.string.cross_profile_widget, icon = R.drawable.widgets_fill0) { onNavigate(CrossProfileWidgetProviders) } FunctionItem(R.string.cross_profile_widget, icon = R.drawable.widgets_fill0) { onNavigate(CrossProfileWidgetProviders) }
} }
if(VERSION.SDK_INT >= 34 && deviceOwner) { if(VERSION.SDK_INT >= 34 && privilege.device) {
FunctionItem(R.string.credential_manager_policy, icon = R.drawable.license_fill0) { onNavigate(CredentialManagerPolicy) } FunctionItem(R.string.credential_manager_policy, icon = R.drawable.license_fill0) { onNavigate(CredentialManagerPolicy) }
} }
FunctionItem(R.string.permitted_accessibility_services, icon = R.drawable.settings_accessibility_fill0) { FunctionItem(R.string.permitted_accessibility_services, icon = R.drawable.settings_accessibility_fill0) {
@@ -222,7 +221,7 @@ fun ApplicationsFeaturesScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Un
} }
FunctionItem(R.string.permitted_ime, icon = R.drawable.keyboard_fill0) { onNavigate(PermittedInputMethods) } FunctionItem(R.string.permitted_ime, icon = R.drawable.keyboard_fill0) { onNavigate(PermittedInputMethods) }
FunctionItem(R.string.enable_system_app, icon = R.drawable.enable_fill0) { onNavigate(EnableSystemApp) } FunctionItem(R.string.enable_system_app, icon = R.drawable.enable_fill0) { onNavigate(EnableSystemApp) }
if(VERSION.SDK_INT >= 34 && (deviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT >= 34 && (privilege.device || privilege.work)) {
FunctionItem(R.string.set_default_dialer, icon = R.drawable.call_fill0) { onNavigate(SetDefaultDialer) } FunctionItem(R.string.set_default_dialer, icon = R.drawable.call_fill0) { onNavigate(SetDefaultDialer) }
} }
} }
@@ -445,9 +444,9 @@ fun DisableUserControlScreen(onNavigateUp: () -> Unit) {
} }
LaunchedEffect(Unit) { refresh() } LaunchedEffect(Unit) { refresh() }
MyLazyScaffold(R.string.disable_user_control, onNavigateUp) { MyLazyScaffold(R.string.disable_user_control, onNavigateUp) {
items(packages, { it.name }) { items(packages, { it.name }) { info ->
ApplicationItem(it) { ApplicationItem(info) {
dpm.setUserControlDisabledPackages(receiver, packages.minus(it).map { it.name }) dpm.setUserControlDisabledPackages(receiver, packages.minus(info).map { it.name })
refresh() refresh()
} }
} }
@@ -477,6 +476,7 @@ fun PermissionsManagerScreen(onNavigateUp: () -> Unit, param: PermissionsManager
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val privilege by myPrivilege.collectAsStateWithLifecycle()
var packageName by remember { mutableStateOf(packageNameParam ?: "") } var packageName by remember { mutableStateOf(packageNameParam ?: "") }
var selectedPermission by remember { mutableStateOf<PermissionItem?>(null) } var selectedPermission by remember { mutableStateOf<PermissionItem?>(null) }
val statusMap = remember { mutableStateMapOf<String, Int>() } val statusMap = remember { mutableStateMapOf<String, Int>() }
@@ -551,7 +551,7 @@ fun PermissionsManagerScreen(onNavigateUp: () -> Unit, param: PermissionsManager
Column { Column {
Text(selectedPermission!!.permission) Text(selectedPermission!!.permission)
Spacer(Modifier.padding(vertical = 4.dp)) Spacer(Modifier.padding(vertical = 4.dp))
if(!(VERSION.SDK_INT >= 31 && selectedPermission!!.profileOwnerRestricted && context.isProfileOwner)) { if(!(VERSION.SDK_INT >= 31 && selectedPermission!!.profileOwnerRestricted && privilege.profile)) {
GrantPermissionItem(R.string.granted, PERMISSION_GRANT_STATE_GRANTED) GrantPermissionItem(R.string.granted, PERMISSION_GRANT_STATE_GRANTED)
} }
GrantPermissionItem(R.string.denied, PERMISSION_GRANT_STATE_DENIED) GrantPermissionItem(R.string.denied, PERMISSION_GRANT_STATE_DENIED)
@@ -581,9 +581,9 @@ fun DisableMeteredDataScreen(onNavigateUp: () -> Unit) {
} }
LaunchedEffect(Unit) { refresh() } LaunchedEffect(Unit) { refresh() }
MyLazyScaffold(R.string.disable_metered_data, onNavigateUp) { MyLazyScaffold(R.string.disable_metered_data, onNavigateUp) {
items(packages, { it.name }) { items(packages, { it.name }) { info ->
ApplicationItem(it) { ApplicationItem(info) {
dpm.setMeteredDataDisabledPackages(receiver, packages.minus(it).map { it.name }) dpm.setMeteredDataDisabledPackages(receiver, packages.minus(info).map { it.name })
refresh() refresh()
} }
} }
@@ -732,9 +732,9 @@ fun KeepUninstalledPackagesScreen(onNavigateUp: () -> Unit) {
} }
LaunchedEffect(Unit) { refresh() } LaunchedEffect(Unit) { refresh() }
MyLazyScaffold(R.string.keep_uninstalled_packages, onNavigateUp) { MyLazyScaffold(R.string.keep_uninstalled_packages, onNavigateUp) {
items(packages, { it.name }) { items(packages, { it.name }) { info ->
ApplicationItem(it) { ApplicationItem(info) {
dpm.setKeepUninstalledPackages(receiver, packages.minus(it).map { it.name }) dpm.setKeepUninstalledPackages(receiver, packages.minus(info).map { it.name })
refresh() refresh()
} }
} }
@@ -798,9 +798,9 @@ fun CrossProfilePackagesScreen(onNavigateUp: () -> Unit) {
} }
LaunchedEffect(Unit) { refresh() } LaunchedEffect(Unit) { refresh() }
MyLazyScaffold(R.string.cross_profile_apps, onNavigateUp) { MyLazyScaffold(R.string.cross_profile_apps, onNavigateUp) {
items(packages, { it.name }) { items(packages, { it.name }) { info ->
ApplicationItem(it) { ApplicationItem(info) {
dpm.setCrossProfilePackages(receiver, packages.minus(it).map { it.name }.toSet()) dpm.setCrossProfilePackages(receiver, packages.minus(info).map { it.name }.toSet())
refresh() refresh()
} }
} }
@@ -1062,6 +1062,7 @@ fun EnableSystemAppScreen(onNavigateUp: () -> Unit) {
) { ) {
Text(stringResource(R.string.enable)) Text(stringResource(R.string.enable))
} }
Notes(R.string.enable_system_app)
} }
} }

View File

@@ -14,17 +14,21 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.IPackageInstaller import android.content.pm.IPackageInstaller
import android.content.pm.PackageInstaller import android.content.pm.PackageInstaller
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import android.graphics.drawable.Icon
import android.os.Build.VERSION import android.os.Build.VERSION
import android.os.UserManager
import android.util.Log import android.util.Log
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.graphics.drawable.toBitmap
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.Receiver import com.bintianqi.owndroid.Receiver
import com.bintianqi.owndroid.SharedPrefs import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.ShortcutsReceiverActivity
import com.bintianqi.owndroid.backToHomeStateFlow import com.bintianqi.owndroid.backToHomeStateFlow
import com.bintianqi.owndroid.myPrivilege
import com.rosan.dhizuku.api.Dhizuku import com.rosan.dhizuku.api.Dhizuku
import com.rosan.dhizuku.api.Dhizuku.binderWrapper import com.rosan.dhizuku.api.Dhizuku.binderWrapper
import com.rosan.dhizuku.api.DhizukuBinderWrapper import com.rosan.dhizuku.api.DhizukuBinderWrapper
@@ -39,8 +43,6 @@ import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonArray import kotlinx.serialization.json.putJsonArray
import java.io.OutputStream import java.io.OutputStream
lateinit var addDeviceAdmin: ActivityResultLauncher<Intent>
val Context.isDeviceOwner: Boolean val Context.isDeviceOwner: Boolean
get() { get() {
val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
@@ -59,11 +61,6 @@ val Context.isProfileOwner: Boolean
return dpm.isProfileOwnerApp("com.bintianqi.owndroid") return dpm.isProfileOwnerApp("com.bintianqi.owndroid")
} }
val Context.isDeviceAdmin: Boolean
get() {
return getDPM().isAdminActive(getReceiver())
}
val Context.dpcPackageName: String val Context.dpcPackageName: String
get() { get() {
return if(SharedPrefs(this).dhizuku) { return if(SharedPrefs(this).dhizuku) {
@@ -73,10 +70,6 @@ val Context.dpcPackageName: String
} }
} }
fun DevicePolicyManager.isOrgProfile(receiver: ComponentName): Boolean {
return VERSION.SDK_INT >= 30 && this.isProfileOwnerApp("com.bintianqi.owndroid") && isManagedProfile(receiver) && isOrganizationOwnedDeviceWithManagedProfile
}
@SuppressLint("PrivateApi") @SuppressLint("PrivateApi")
private fun binderWrapperDevicePolicyManager(appContext: Context): DevicePolicyManager? { private fun binderWrapperDevicePolicyManager(appContext: Context): DevicePolicyManager? {
try { try {
@@ -544,10 +537,10 @@ fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? {
fun setDefaultAffiliationID(context: Context) { fun setDefaultAffiliationID(context: Context) {
if(VERSION.SDK_INT < 26) return if(VERSION.SDK_INT < 26) return
val sp = SharedPrefs(context) val sp = SharedPrefs(context)
val privilege = myPrivilege.value
if(!sp.isDefaultAffiliationIdSet) { if(!sp.isDefaultAffiliationIdSet) {
try { try {
val um = context.getSystemService(Context.USER_SERVICE) as UserManager if(privilege.device || (!privilege.primary && privilege.profile)) {
if(context.isDeviceOwner || (!um.isSystemUser && context.isProfileOwner)) {
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val affiliationIDs = dpm.getAffiliationIds(receiver) val affiliationIDs = dpm.getAffiliationIds(receiver)
@@ -596,3 +589,31 @@ fun parsePackageInstallerMessage(context: Context, result: Intent): String {
else -> "" else -> ""
} + statusMessage.let { if(it == null) "" else "\n$it" } } + statusMessage.let { if(it == null) "" else "\n$it" }
} }
fun handlePrivilegeChange(context: Context) {
val privilege = myPrivilege.value
val activated = privilege.device || privilege.profile
if(activated) {
if(!privilege.dhizuku) {
setDefaultAffiliationID(context)
if(VERSION.SDK_INT >= 25) {
val sm = context.getSystemService(ShortcutManager::class.java)
val lockIntent = Intent("com.bintianqi.owndroid.action.LOCK")
.setComponent(ComponentName(context, ShortcutsReceiverActivity::class.java))
val shortcut = ShortcutInfo.Builder(context, "LockScreen")
.setShortLabel(context.getString(R.string.lock_now))
.setIcon(Icon.createWithBitmap(context.getDrawable(R.drawable.screen_lock_portrait_fill0)?.toBitmap()))
.setIntent(lockIntent)
sm.addDynamicShortcuts(listOf(shortcut.build()))
}
}
} else {
SharedPrefs(context).isDefaultAffiliationIdSet = false
if(VERSION.SDK_INT >= 25) {
val sm = context.getSystemService(ShortcutManager::class.java)
sm.removeDynamicShortcuts(listOf("LockScreen"))
}
SharedPrefs(context).isApiEnabled = false
}
}

View File

@@ -26,7 +26,6 @@ import android.net.IpConfiguration
import android.net.LinkAddress import android.net.LinkAddress
import android.net.ProxyInfo import android.net.ProxyInfo
import android.net.StaticIpConfiguration import android.net.StaticIpConfiguration
import android.net.Uri
import android.net.wifi.WifiConfiguration import android.net.wifi.WifiConfiguration
import android.net.wifi.WifiManager import android.net.wifi.WifiManager
import android.net.wifi.WifiSsid import android.net.wifi.WifiSsid
@@ -124,14 +123,16 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.ChoosePackageContract import com.bintianqi.owndroid.ChoosePackageContract
import com.bintianqi.owndroid.HorizontalPadding import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.formatDate import com.bintianqi.owndroid.formatDate
import com.bintianqi.owndroid.formatFileSize import com.bintianqi.owndroid.formatFileSize
import com.bintianqi.owndroid.humanReadableDate import com.bintianqi.owndroid.humanReadableDate
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.ErrorDialog import com.bintianqi.owndroid.ui.ErrorDialog
@@ -163,29 +164,24 @@ import kotlin.reflect.jvm.jvmErasure
@Composable @Composable
fun NetworkScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) { fun NetworkScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
val context = LocalContext.current val privilege by myPrivilege.collectAsStateWithLifecycle()
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
val dhizuku = SharedPrefs(context).dhizuku
MyScaffold(R.string.network, onNavigateUp, 0.dp) { MyScaffold(R.string.network, onNavigateUp, 0.dp) {
if(!dhizuku) FunctionItem(R.string.wifi, icon = R.drawable.wifi_fill0) { onNavigate(WiFi) } if(!privilege.dhizuku) FunctionItem(R.string.wifi, icon = R.drawable.wifi_fill0) { onNavigate(WiFi) }
if(VERSION.SDK_INT >= 30) { if(VERSION.SDK_INT >= 30) {
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { onNavigate(NetworkOptions) } FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { onNavigate(NetworkOptions) }
} }
if(VERSION.SDK_INT >= 23 && !dhizuku && (deviceOwner || profileOwner)) if(VERSION.SDK_INT >= 23 && !privilege.dhizuku && (privilege.device || privilege.profile))
FunctionItem(R.string.network_stats, icon = R.drawable.query_stats_fill0) { onNavigate(QueryNetworkStats) } FunctionItem(R.string.network_stats, icon = R.drawable.query_stats_fill0) { onNavigate(QueryNetworkStats) }
if(VERSION.SDK_INT >= 29 && deviceOwner) { if(VERSION.SDK_INT >= 29 && privilege.device) {
FunctionItem(R.string.private_dns, icon = R.drawable.dns_fill0) { onNavigate(PrivateDns) } FunctionItem(R.string.private_dns, icon = R.drawable.dns_fill0) { onNavigate(PrivateDns) }
} }
if(VERSION.SDK_INT >= 24) { if(VERSION.SDK_INT >= 24) {
FunctionItem(R.string.always_on_vpn, icon = R.drawable.vpn_key_fill0) { onNavigate(AlwaysOnVpnPackage) } FunctionItem(R.string.always_on_vpn, icon = R.drawable.vpn_key_fill0) { onNavigate(AlwaysOnVpnPackage) }
} }
if(deviceOwner) { if(privilege.device) {
FunctionItem(R.string.recommended_global_proxy, icon = R.drawable.vpn_key_fill0) { onNavigate(RecommendedGlobalProxy) } FunctionItem(R.string.recommended_global_proxy, icon = R.drawable.vpn_key_fill0) { onNavigate(RecommendedGlobalProxy) }
} }
if(VERSION.SDK_INT >= 26 && !dhizuku && (deviceOwner || (profileOwner && dpm.isManagedProfile(receiver)))) { if(VERSION.SDK_INT >= 26 && !privilege.dhizuku && (privilege.device || privilege.work)) {
FunctionItem(R.string.network_logging, icon = R.drawable.description_fill0) { onNavigate(NetworkLogging) } FunctionItem(R.string.network_logging, icon = R.drawable.description_fill0) { onNavigate(NetworkLogging) }
} }
if(VERSION.SDK_INT >= 31) { if(VERSION.SDK_INT >= 31) {
@@ -194,7 +190,7 @@ fun NetworkScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
if(VERSION.SDK_INT >= 33) { if(VERSION.SDK_INT >= 33) {
FunctionItem(R.string.preferential_network_service, icon = R.drawable.globe_fill0) { onNavigate(PreferentialNetworkService) } FunctionItem(R.string.preferential_network_service, icon = R.drawable.globe_fill0) { onNavigate(PreferentialNetworkService) }
} }
if(VERSION.SDK_INT >= 28 && deviceOwner) { if(VERSION.SDK_INT >= 28 && privilege.device) {
FunctionItem(R.string.override_apn, icon = R.drawable.cell_tower_fill0) { onNavigate(OverrideApn) } FunctionItem(R.string.override_apn, icon = R.drawable.cell_tower_fill0) { onNavigate(OverrideApn) }
} }
} }
@@ -207,10 +203,10 @@ fun NetworkOptionsScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner val privilege by myPrivilege.collectAsStateWithLifecycle()
var dialog by remember { mutableIntStateOf(0) } var dialog by remember { mutableIntStateOf(0) }
MyScaffold(R.string.options, onNavigateUp, 0.dp) { MyScaffold(R.string.options, onNavigateUp, 0.dp) {
if(VERSION.SDK_INT>=30 && (deviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT >= 30 && (privilege.device || privilege.org)) {
SwitchItem(R.string.lockdown_admin_configured_network, icon = R.drawable.wifi_password_fill0, SwitchItem(R.string.lockdown_admin_configured_network, icon = R.drawable.wifi_password_fill0,
getState = { dpm.hasLockdownAdminConfiguredNetworks(receiver) }, onCheckedChange = { dpm.setConfiguredNetworksLockdownState(receiver,it) }, getState = { dpm.hasLockdownAdminConfiguredNetworks(receiver) }, onCheckedChange = { dpm.setConfiguredNetworksLockdownState(receiver,it) },
onClickBlank = { dialog = 1 } onClickBlank = { dialog = 1 }
@@ -265,9 +261,8 @@ fun WifiScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateTo
} }
HorizontalPager(state = pagerState, verticalAlignment = Alignment.Top) { page -> HorizontalPager(state = pagerState, verticalAlignment = Alignment.Top) { page ->
if(page == 0) { if(page == 0) {
val wm = context.getSystemService(Context.WIFI_SERVICE) as WifiManager val wm = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val deviceOwner = context.isDeviceOwner val privilege by myPrivilege.collectAsStateWithLifecycle()
val orgProfileOwner = context.getDPM().isOrgProfile(context.getReceiver())
@Suppress("DEPRECATION") Column( @Suppress("DEPRECATION") Column(
modifier = Modifier.fillMaxSize().padding(top = 12.dp) modifier = Modifier.fillMaxSize().padding(top = 12.dp)
) { ) {
@@ -299,10 +294,10 @@ fun WifiScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateTo
Text(stringResource(R.string.reconnect)) Text(stringResource(R.string.reconnect))
} }
} }
if(VERSION.SDK_INT >= 24 && (deviceOwner || orgProfileOwner)) { if(VERSION.SDK_INT >= 24 && (privilege.device || privilege.org)) {
FunctionItem(R.string.wifi_mac_address) { wifiMacDialog = true } FunctionItem(R.string.wifi_mac_address) { wifiMacDialog = true }
} }
if(VERSION.SDK_INT >= 33 && (deviceOwner || orgProfileOwner)) { if(VERSION.SDK_INT >= 33 && (privilege.device || privilege.org)) {
FunctionItem(R.string.min_wifi_security_level) { onNavigate(WifiSecurityLevel) } FunctionItem(R.string.min_wifi_security_level) { onNavigate(WifiSecurityLevel) }
FunctionItem(R.string.wifi_ssid_policy) { onNavigate(WifiSsidPolicyScreen) } FunctionItem(R.string.wifi_ssid_policy) { onNavigate(WifiSsidPolicyScreen) }
} }
@@ -315,7 +310,6 @@ fun WifiScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateTo
} }
} }
if(wifiMacDialog && VERSION.SDK_INT >= 24) { if(wifiMacDialog && VERSION.SDK_INT >= 24) {
val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
AlertDialog( AlertDialog(
@@ -344,7 +338,7 @@ fun WifiScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateTo
@Composable @Composable
private fun SavedNetworks(onNavigateToUpdateNetwork: (Bundle) -> Unit) { private fun SavedNetworks(onNavigateToUpdateNetwork: (Bundle) -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val wm = context.getSystemService(Context.WIFI_SERVICE) as WifiManager val wm = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val configuredNetworks = remember { mutableStateListOf<WifiConfiguration>() } val configuredNetworks = remember { mutableStateListOf<WifiConfiguration>() }
var networkDetailsDialog by remember { mutableIntStateOf(-1) } // -1:Hidden, 0+:Index of configuredNetworks var networkDetailsDialog by remember { mutableIntStateOf(-1) } // -1:Hidden, 0+:Index of configuredNetworks
val coroutine = rememberCoroutineScope() val coroutine = rememberCoroutineScope()
@@ -688,7 +682,7 @@ private fun AddNetworkScreen(wifiConfig: WifiConfiguration? = null, onNavigateUp
} }
Button( Button(
onClick = { onClick = {
val wm = context.getSystemService(Context.WIFI_SERVICE) as WifiManager val wm = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
try { try {
val config = WifiConfiguration() val config = WifiConfiguration()
config.status = status config.status = status
@@ -899,7 +893,7 @@ fun NetworkStats.toBucketList(): List<NetworkStats.Bucket> {
@Composable @Composable
fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkStatsViewer) -> Unit) { fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkStatsViewer) -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val deviceOwner = context.isDeviceOwner val privilege by myPrivilege.collectAsStateWithLifecycle()
val fm = LocalFocusManager.current val fm = LocalFocusManager.current
val nsm = context.getSystemService(NetworkStatsManager::class.java) val nsm = context.getSystemService(NetworkStatsManager::class.java)
val coroutine = rememberCoroutineScope() val coroutine = rememberCoroutineScope()
@@ -971,7 +965,7 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
NetworkStatsTarget.entries.forEach { NetworkStatsTarget.entries.forEach {
if( if(
VERSION.SDK_INT >= it.minApi && VERSION.SDK_INT >= it.minApi &&
(deviceOwner || it != NetworkStatsTarget.Device) && (privilege.device || it != NetworkStatsTarget.Device) &&
((queryType == 1 && (it == NetworkStatsTarget.Device || it == NetworkStatsTarget.User)) || ((queryType == 1 && (it == NetworkStatsTarget.Device || it == NetworkStatsTarget.User)) ||
(queryType == 2 && (it == NetworkStatsTarget.Uid || it == NetworkStatsTarget.UidTag || it == NetworkStatsTarget.UidTagState))) (queryType == 2 && (it == NetworkStatsTarget.Uid || it == NetworkStatsTarget.UidTag || it == NetworkStatsTarget.UidTagState)))
) DropdownMenuItem( ) DropdownMenuItem(
@@ -1120,7 +1114,7 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
if(VERSION.SDK_INT >= 24 && (target == NetworkStatsTarget.UidTag || target == NetworkStatsTarget.UidTagState)) if(VERSION.SDK_INT >= 24 && (target == NetworkStatsTarget.UidTag || target == NetworkStatsTarget.UidTagState))
ExposedDropdownMenuBox( ExposedDropdownMenuBox(
activeTextField == NetworkStatsActiveTextField.Tag, activeTextField == NetworkStatsActiveTextField.Tag,
{ activeTextField == if(it) NetworkStatsActiveTextField.Tag else NetworkStatsActiveTextField.None } { activeTextField = if(it) NetworkStatsActiveTextField.Tag else NetworkStatsActiveTextField.None }
) { ) {
var tagText by rememberSaveable { mutableStateOf(context.getString(R.string.all)) } var tagText by rememberSaveable { mutableStateOf(context.getString(R.string.all)) }
var readOnly by rememberSaveable { mutableStateOf(true) } var readOnly by rememberSaveable { mutableStateOf(true) }
@@ -1596,7 +1590,7 @@ fun RecommendedGlobalProxyScreen(onNavigateUp: () -> Unit) {
Toast.makeText(context, R.string.invalid_config, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.invalid_config, Toast.LENGTH_SHORT).show()
return@Button return@Button
} }
val uri = Uri.parse(proxyUri) val uri = proxyUri.toUri()
val port: Int val port: Int
try { try {
port = proxyPort.toInt() port = proxyPort.toInt()
@@ -1827,8 +1821,8 @@ fun AddPreferentialNetworkServiceConfigScreen(route: AddPreferentialNetworkServi
title = R.string.block_non_matching_networks, title = R.string.block_non_matching_networks,
state = blockNonMatching, onCheckedChange = { blockNonMatching = it }, padding = false state = blockNonMatching, onCheckedChange = { blockNonMatching = it }, padding = false
) )
val includedUidsLegal = includedUids.lines().filter { it.isNotBlank() }.let { val includedUidsLegal = includedUids.lines().filter { it.isNotBlank() }.let { uid ->
it.isEmpty() || (it.all { it.toIntOrNull() != null } && excludedUids.isBlank()) uid.isEmpty() || (uid.all { it.toIntOrNull() != null } && excludedUids.isBlank())
} }
OutlinedTextField( OutlinedTextField(
value = includedUids, onValueChange = { includedUids = it }, minLines = 2, value = includedUids, onValueChange = { includedUids = it }, minLines = 2,
@@ -1837,8 +1831,8 @@ fun AddPreferentialNetworkServiceConfigScreen(route: AddPreferentialNetworkServi
isError = !includedUidsLegal, isError = !includedUidsLegal,
modifier = Modifier.fillMaxWidth().padding(bottom = 6.dp) modifier = Modifier.fillMaxWidth().padding(bottom = 6.dp)
) )
val excludedUidsLegal = excludedUids.lines().filter { it.isNotBlank() }.let { val excludedUidsLegal = excludedUids.lines().filter { it.isNotBlank() }.let { uid ->
it.isEmpty() || (it.all { it.toIntOrNull() != null } && includedUids.isBlank()) uid.isEmpty() || (uid.all { it.toIntOrNull() != null } && includedUids.isBlank())
} }
OutlinedTextField( OutlinedTextField(
value = excludedUids, onValueChange = { excludedUids = it }, minLines = 2, value = excludedUids, onValueChange = { excludedUids = it }, minLines = 2,
@@ -1852,7 +1846,7 @@ fun AddPreferentialNetworkServiceConfigScreen(route: AddPreferentialNetworkServi
try { try {
val config = PreferentialNetworkServiceConfig.Builder().apply { val config = PreferentialNetworkServiceConfig.Builder().apply {
setEnabled(enabled) setEnabled(enabled)
if(enabled) setNetworkId(id.toInt()) if(enabled) setNetworkId(id)
setFallbackToDefaultConnectionAllowed(allowFallback) setFallbackToDefaultConnectionAllowed(allowFallback)
setExcludedUids(excludedUids.lines().filter { it.isNotBlank() }.map { it.toInt() }.toIntArray()) setExcludedUids(excludedUids.lines().filter { it.isNotBlank() }.map { it.toInt() }.toIntArray())
setIncludedUids(includedUids.lines().filter { it.isNotBlank() }.map { it.toInt() }.toIntArray()) setIncludedUids(includedUids.lines().filter { it.isNotBlank() }.map { it.toInt() }.toIntArray())
@@ -1957,7 +1951,7 @@ private val apnTypes = listOf(
@Serializable object AddApnSetting @Serializable object AddApnSetting
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) @OptIn(ExperimentalLayoutApi::class)
@RequiresApi(28) @RequiresApi(28)
@Composable @Composable
fun AddApnSettingScreen(origin: ApnSetting?, onNavigateUp: () -> Unit) { fun AddApnSettingScreen(origin: ApnSetting?, onNavigateUp: () -> Unit) {
@@ -2099,14 +2093,14 @@ fun AddApnSettingScreen(origin: ApnSetting?, onNavigateUp: () -> Unit) {
OutlinedTextField( OutlinedTextField(
mtuV4, { mtuV4 = it }, Modifier.fillMaxWidth(0.49F), mtuV4, { mtuV4 = it }, Modifier.fillMaxWidth(0.49F),
label = { Text("MTU (IPv4)") }, label = { Text("MTU (IPv4)") },
isError = !mtuV4.isEmpty() && mtuV4.toIntOrNull() == null, isError = mtuV4.isNotEmpty() && mtuV4.toIntOrNull() == null,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Next), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Next),
keyboardActions = KeyboardActions { fr.requestFocus() } keyboardActions = KeyboardActions { fr.requestFocus() }
) )
OutlinedTextField( OutlinedTextField(
mtuV6, { mtuV6 = it }, Modifier.focusRequester(fr).fillMaxWidth(0.96F), mtuV6, { mtuV6 = it }, Modifier.focusRequester(fr).fillMaxWidth(0.96F),
label = { Text("MTU (IPv6)") }, label = { Text("MTU (IPv6)") },
isError = !mtuV6.isEmpty() && mtuV6.toIntOrNull() == null, isError = mtuV6.isNotEmpty() && mtuV6.toIntOrNull() == null,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions { fm.clearFocus() } keyboardActions = KeyboardActions { fm.clearFocus() }
) )
@@ -2194,7 +2188,7 @@ fun AddApnSettingScreen(origin: ApnSetting?, onNavigateUp: () -> Unit) {
setMmsProxyAddress(mmsProxyAddress) setMmsProxyAddress(mmsProxyAddress)
mmsProxyPort.toIntOrNull()?.let { setMmsProxyPort(it) } mmsProxyPort.toIntOrNull()?.let { setMmsProxyPort(it) }
} }
setMmsc(Uri.parse(mmsc)) setMmsc(mmsc.toUri())
if(VERSION.SDK_INT >= 33) { if(VERSION.SDK_INT >= 33) {
mtuV4.toIntOrNull()?.let { setMtuV4(it) } mtuV4.toIntOrNull()?.let { setMtuV4(it) }
mtuV6.toIntOrNull()?.let { setMtuV6(it) } mtuV6.toIntOrNull()?.let { setMtuV6(it) }
@@ -2238,7 +2232,7 @@ fun AddApnSettingScreen(origin: ApnSetting?, onNavigateUp: () -> Unit) {
AlertDialog( AlertDialog(
title = { Text(if(dialog == 1) "Proxy" else "MMS proxy") }, title = { Text(if(dialog == 1) "Proxy" else "MMS proxy") },
text = { text = {
val fm = LocalFocusManager.current val focusManager = LocalFocusManager.current
Column { Column {
OutlinedTextField( OutlinedTextField(
address, { address = it }, Modifier.fillMaxWidth().padding(bottom = 4.dp), address, { address = it }, Modifier.fillMaxWidth().padding(bottom = 4.dp),
@@ -2253,7 +2247,7 @@ fun AddApnSettingScreen(origin: ApnSetting?, onNavigateUp: () -> Unit) {
isError = port.isNotEmpty() && port.toIntOrNull() == null, isError = port.isNotEmpty() && port.toIntOrNull() == null,
label = { Text(stringResource(R.string.port)) }, label = { Text(stringResource(R.string.port)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions { fm.clearFocus() } keyboardActions = KeyboardActions { focusManager.clearFocus() }
) )
} }
}, },

View File

@@ -65,9 +65,11 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat.startActivity import androidx.core.content.ContextCompat.startActivity
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.HorizontalPadding import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.SharedPrefs import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem
@@ -86,38 +88,30 @@ import kotlinx.serialization.Serializable
@Composable @Composable
fun PasswordScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) { fun PasswordScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val deviceAdmin = context.isDeviceAdmin val privilege by myPrivilege.collectAsStateWithLifecycle()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
var dialog by remember { mutableIntStateOf(0) } var dialog by remember { mutableIntStateOf(0) }
MyScaffold(R.string.password_and_keyguard, onNavigateUp, 0.dp) { MyScaffold(R.string.password_and_keyguard, onNavigateUp, 0.dp) {
FunctionItem(R.string.password_info, icon = R.drawable.info_fill0) { onNavigate(PasswordInfo) } FunctionItem(R.string.password_info, icon = R.drawable.info_fill0) { onNavigate(PasswordInfo) }
if(SharedPrefs(context).displayDangerousFeatures) { if(SharedPrefs(context).displayDangerousFeatures) {
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) { if(VERSION.SDK_INT >= 26) {
FunctionItem(R.string.reset_password_token, icon = R.drawable.key_vertical_fill0) { onNavigate(ResetPasswordToken) } FunctionItem(R.string.reset_password_token, icon = R.drawable.key_vertical_fill0) { onNavigate(ResetPasswordToken) }
} }
if(deviceAdmin || deviceOwner || profileOwner) {
FunctionItem(R.string.reset_password, icon = R.drawable.lock_reset_fill0) { onNavigate(ResetPassword) } FunctionItem(R.string.reset_password, icon = R.drawable.lock_reset_fill0) { onNavigate(ResetPassword) }
} }
} if(VERSION.SDK_INT >= 31) {
if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) {
FunctionItem(R.string.required_password_complexity, icon = R.drawable.password_fill0) { onNavigate(RequiredPasswordComplexity) } FunctionItem(R.string.required_password_complexity, icon = R.drawable.password_fill0) { onNavigate(RequiredPasswordComplexity) }
} }
if(deviceAdmin) {
FunctionItem(R.string.disable_keyguard_features, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(KeyguardDisabledFeatures) } FunctionItem(R.string.disable_keyguard_features, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(KeyguardDisabledFeatures) }
} if(privilege.device) {
if(deviceOwner) {
FunctionItem(R.string.max_time_to_lock, icon = R.drawable.schedule_fill0) { dialog = 1 } FunctionItem(R.string.max_time_to_lock, icon = R.drawable.schedule_fill0) { dialog = 1 }
FunctionItem(R.string.pwd_expiration_timeout, icon = R.drawable.lock_clock_fill0) { dialog = 3 } FunctionItem(R.string.pwd_expiration_timeout, icon = R.drawable.lock_clock_fill0) { dialog = 3 }
FunctionItem(R.string.max_pwd_fail, icon = R.drawable.no_encryption_fill0) { dialog = 4 } FunctionItem(R.string.max_pwd_fail, icon = R.drawable.no_encryption_fill0) { dialog = 4 }
} }
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) { if(VERSION.SDK_INT >= 26) {
FunctionItem(R.string.required_strong_auth_timeout, icon = R.drawable.fingerprint_off_fill0) { dialog = 2 } FunctionItem(R.string.required_strong_auth_timeout, icon = R.drawable.fingerprint_off_fill0) { dialog = 2 }
} }
if(deviceAdmin){
FunctionItem(R.string.pwd_history, icon = R.drawable.history_fill0) { dialog = 5 } FunctionItem(R.string.pwd_history, icon = R.drawable.history_fill0) { dialog = 5 }
} if(VERSION.SDK_INT < 31) {
if(VERSION.SDK_INT < 31 && (deviceOwner || profileOwner)) {
FunctionItem(R.string.required_password_quality, icon = R.drawable.password_fill0) { onNavigate(RequiredPasswordQuality) } FunctionItem(R.string.required_password_quality, icon = R.drawable.password_fill0) { onNavigate(RequiredPasswordQuality) }
} }
} }
@@ -217,8 +211,7 @@ fun PasswordInfoScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner val privilege by myPrivilege.collectAsStateWithLifecycle()
val profileOwner = context.isProfileOwner
var dialog by remember { mutableIntStateOf(0) } // 0:none, 1:password complexity var dialog by remember { mutableIntStateOf(0) } // 0:none, 1:password complexity
MyScaffold(R.string.password_info, onNavigateUp, 0.dp) { MyScaffold(R.string.password_info, onNavigateUp, 0.dp) {
if(VERSION.SDK_INT >= 29) { if(VERSION.SDK_INT >= 29) {
@@ -231,10 +224,8 @@ fun PasswordInfoScreen(onNavigateUp: () -> Unit) {
} }
InfoItem(R.string.current_password_complexity, text, true) { dialog = 1 } InfoItem(R.string.current_password_complexity, text, true) { dialog = 1 }
} }
if(deviceOwner || profileOwner) {
InfoItem(R.string.password_sufficient, dpm.isActivePasswordSufficient.yesOrNo) InfoItem(R.string.password_sufficient, dpm.isActivePasswordSufficient.yesOrNo)
} if(VERSION.SDK_INT >= 28 && privilege.work) {
if(VERSION.SDK_INT >= 28 && profileOwner && dpm.isManagedProfile(receiver)) {
InfoItem(R.string.unified_password, dpm.isUsingUnifiedPassword(receiver).yesOrNo) InfoItem(R.string.unified_password, dpm.isUsingUnifiedPassword(receiver).yesOrNo)
} }
} }
@@ -367,7 +358,7 @@ fun ResetPasswordScreen(onNavigateUp: () -> Unit) {
focusMgr.clearFocus() focusMgr.clearFocus()
}, },
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError), colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError),
enabled = tokenByteArray.size >=32 && password.length !in 1..3 && (context.isDeviceOwner || context.isProfileOwner), enabled = tokenByteArray.size >=32 && password.length !in 1..3,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text(stringResource(R.string.reset_password_with_token)) Text(stringResource(R.string.reset_password_with_token))

View File

@@ -1,18 +1,14 @@
package com.bintianqi.owndroid.dpm package com.bintianqi.owndroid.dpm
import android.annotation.SuppressLint
import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager
import android.content.ActivityNotFoundException
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Binder import android.os.Binder
import android.os.Build.VERSION import android.os.Build.VERSION
import android.os.Bundle import android.os.Bundle
import android.os.IBinder import android.os.IBinder
import android.os.RemoteException import android.os.RemoteException
import android.os.UserManager
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.annotation.Keep import androidx.annotation.Keep
@@ -21,7 +17,6 @@ import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.text.selection.SelectionContainer
@@ -43,13 +38,16 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.ChoosePackageContract import com.bintianqi.owndroid.ChoosePackageContract
import com.bintianqi.owndroid.HorizontalPadding import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.SharedPrefs import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.backToHomeStateFlow import com.bintianqi.owndroid.backToHomeStateFlow
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.* import com.bintianqi.owndroid.ui.*
import com.bintianqi.owndroid.updatePrivilege
import com.bintianqi.owndroid.writeClipBoard import com.bintianqi.owndroid.writeClipBoard
import com.bintianqi.owndroid.yesOrNo import com.bintianqi.owndroid.yesOrNo
import com.rosan.dhizuku.api.Dhizuku import com.rosan.dhizuku.api.Dhizuku
@@ -61,19 +59,15 @@ import rikka.sui.Sui
@Serializable object Permissions @Serializable object Permissions
@SuppressLint("NewApi")
@Composable @Composable
fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateToShizuku: (Bundle) -> Unit) { fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateToShizuku: (Bundle) -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val deviceAdmin = context.isDeviceAdmin val privilege by myPrivilege.collectAsStateWithLifecycle()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
var dialog by remember { mutableIntStateOf(0) } var dialog by remember { mutableIntStateOf(0) }
var bindingShizuku by remember { mutableStateOf(false) } var bindingShizuku by remember { mutableStateOf(false) }
val enrollmentSpecificId = if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) dpm.enrollmentSpecificId else "" val enrollmentSpecificId = if(VERSION.SDK_INT >= 31 && (privilege.device || privilege.profile)) dpm.enrollmentSpecificId else ""
MyScaffold(R.string.permissions, onNavigateUp, 0.dp) { MyScaffold(R.string.permissions, onNavigateUp, 0.dp) {
if(!dpm.isDeviceOwnerApp(context.packageName)) { if(!dpm.isDeviceOwnerApp(context.packageName)) {
SwitchItem( SwitchItem(
@@ -83,19 +77,15 @@ fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNav
onClickBlank = { dialog = 4 } onClickBlank = { dialog = 4 }
) )
} }
if(privilege.profile || !privilege.primary) {
FunctionItem( FunctionItem(
R.string.device_admin, stringResource(if(deviceAdmin) R.string.activated else R.string.deactivated), R.string.profile_owner, stringResource(if(privilege.profile) R.string.activated else R.string.deactivated),
operation = { onNavigate(DeviceAdmin) }
)
if(profileOwner || !userManager.isSystemUser) {
FunctionItem(
R.string.profile_owner, stringResource(if(profileOwner) R.string.activated else R.string.deactivated),
operation = { onNavigate(ProfileOwner) } operation = { onNavigate(ProfileOwner) }
) )
} }
if(!profileOwner && userManager.isSystemUser) { if(!privilege.profile && privilege.primary) {
FunctionItem( FunctionItem(
R.string.device_owner, stringResource(if(deviceOwner) R.string.activated else R.string.deactivated), R.string.device_owner, stringResource(if(privilege.device) R.string.activated else R.string.deactivated),
operation = { onNavigate(DeviceOwner) } operation = { onNavigate(DeviceOwner) }
) )
} }
@@ -130,25 +120,24 @@ fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNav
Toast.makeText(context, R.string.shizuku_not_started, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.shizuku_not_started, Toast.LENGTH_SHORT).show()
} }
} }
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) if(VERSION.SDK_INT >= 26) FunctionItem(R.string.delegated_admins) { onNavigate(DelegatedAdmins) }
FunctionItem(R.string.delegated_admins) { onNavigate(DelegatedAdmins) }
FunctionItem(R.string.device_info, icon = R.drawable.perm_device_information_fill0) { onNavigate(DeviceInfo) } FunctionItem(R.string.device_info, icon = R.drawable.perm_device_information_fill0) { onNavigate(DeviceInfo) }
if(VERSION.SDK_INT >= 24 && (profileOwner || (VERSION.SDK_INT >= 26 && deviceOwner))) { if(VERSION.SDK_INT >= 24 && (privilege.profile || (VERSION.SDK_INT >= 26 && privilege.device))) {
FunctionItem(R.string.org_name, icon = R.drawable.corporate_fare_fill0) { dialog = 2 } FunctionItem(R.string.org_name, icon = R.drawable.corporate_fare_fill0) { dialog = 2 }
} }
if(VERSION.SDK_INT >= 31 && (profileOwner || deviceOwner)) { if(VERSION.SDK_INT >= 31) {
FunctionItem(R.string.org_id, icon = R.drawable.corporate_fare_fill0) { dialog = 3 } FunctionItem(R.string.org_id, icon = R.drawable.corporate_fare_fill0) { dialog = 3 }
} }
if(enrollmentSpecificId != "") { if(enrollmentSpecificId != "") {
FunctionItem(R.string.enrollment_specific_id, icon = R.drawable.id_card_fill0) { dialog = 1 } FunctionItem(R.string.enrollment_specific_id, icon = R.drawable.id_card_fill0) { dialog = 1 }
} }
if(VERSION.SDK_INT >= 24 && (deviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT >= 24 && (privilege.device || privilege.org)) {
FunctionItem(R.string.lock_screen_info, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(LockScreenInfo) } FunctionItem(R.string.lock_screen_info, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(LockScreenInfo) }
} }
if(VERSION.SDK_INT >= 24 && deviceAdmin) { if(VERSION.SDK_INT >= 24) {
FunctionItem(R.string.support_messages, icon = R.drawable.chat_fill0) { onNavigate(SupportMessage) } FunctionItem(R.string.support_messages, icon = R.drawable.chat_fill0) { onNavigate(SupportMessage) }
} }
if(VERSION.SDK_INT >= 28 && (deviceOwner || profileOwner)) { if(VERSION.SDK_INT >= 28) {
FunctionItem(R.string.transfer_ownership, icon = R.drawable.admin_panel_settings_fill0) { onNavigate(TransferOwnership) } FunctionItem(R.string.transfer_ownership, icon = R.drawable.admin_panel_settings_fill0) { onNavigate(TransferOwnership) }
} }
} }
@@ -174,7 +163,7 @@ fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNav
text = { text = {
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
if(dialog == 1) input = dpm.enrollmentSpecificId if(dialog == 1 && VERSION.SDK_INT >= 31) input = dpm.enrollmentSpecificId
} }
Column { Column {
if(dialog != 4) OutlinedTextField( if(dialog != 4) OutlinedTextField(
@@ -220,8 +209,8 @@ fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNav
TextButton( TextButton(
onClick = { onClick = {
try { try {
if(dialog == 2) dpm.setOrganizationName(receiver, input) if(dialog == 2 && VERSION.SDK_INT >= 24) dpm.setOrganizationName(receiver, input)
if(dialog == 3) dpm.setOrganizationId(input) if(dialog == 3 && VERSION.SDK_INT >= 31) dpm.setOrganizationId(input)
dialog = 0 dialog = 0
} catch(_: IllegalStateException) { } catch(_: IllegalStateException) {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
@@ -250,6 +239,7 @@ private fun toggleDhizukuMode(status: Boolean, context: Context) {
if(dhizukuPermissionGranted()) { if(dhizukuPermissionGranted()) {
sp.dhizuku = true sp.dhizuku = true
Dhizuku.init(context) Dhizuku.init(context)
updatePrivilege(context)
backToHomeStateFlow.value = true backToHomeStateFlow.value = true
} else { } else {
Dhizuku.requestPermission(object: DhizukuRequestPermissionListener() { Dhizuku.requestPermission(object: DhizukuRequestPermissionListener() {
@@ -258,6 +248,7 @@ private fun toggleDhizukuMode(status: Boolean, context: Context) {
if(grantResult == PackageManager.PERMISSION_GRANTED) { if(grantResult == PackageManager.PERMISSION_GRANTED) {
sp.dhizuku = true sp.dhizuku = true
Dhizuku.init(context) Dhizuku.init(context)
updatePrivilege(context)
backToHomeStateFlow.value = true backToHomeStateFlow.value = true
} else { } else {
dhizukuErrorStatus.value = 2 dhizukuErrorStatus.value = 2
@@ -311,65 +302,6 @@ fun LockScreenInfoScreen(onNavigateUp: () -> Unit) {
} }
} }
@Serializable object DeviceAdmin
@Composable
fun DeviceAdminScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
var deactivateDialog by remember { mutableStateOf(false) }
val deviceAdmin = context.isDeviceAdmin
MyScaffold(R.string.device_admin, onNavigateUp) {
Text(text = stringResource(if(context.isDeviceAdmin) R.string.activated else R.string.deactivated), style = typography.titleLarge)
Spacer(Modifier.padding(vertical = 5.dp))
AnimatedVisibility(deviceAdmin) {
Button(
onClick = { deactivateDialog = true },
enabled = !context.isProfileOwner && !context.isDeviceOwner,
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError)
) {
Text(stringResource(R.string.deactivate))
}
}
AnimatedVisibility(!deviceAdmin) {
Column {
Button(onClick = { activateDeviceAdmin(context, receiver) }, modifier = Modifier.fillMaxWidth()) {
Text(stringResource(R.string.activate_jump))
}
Spacer(Modifier.padding(vertical = 5.dp))
SelectionContainer {
Text(text = stringResource(R.string.activate_device_admin_command))
}
CopyTextButton(R.string.copy_command, stringResource(R.string.activate_device_admin_command))
}
}
}
if(deactivateDialog) {
AlertDialog(
title = { Text(stringResource(R.string.deactivate)) },
onDismissRequest = { deactivateDialog = false },
dismissButton = {
TextButton(
onClick = { deactivateDialog = false }
) {
Text(stringResource(R.string.cancel))
}
},
confirmButton = {
TextButton(
onClick = {
dpm.removeActiveAdmin(receiver)
deactivateDialog = false
}
) {
Text(stringResource(R.string.confirm))
}
}
)
}
}
@Serializable object ProfileOwner @Serializable object ProfileOwner
@Composable @Composable
@@ -378,7 +310,8 @@ fun ProfileOwnerScreen(onNavigateUp: () -> Unit) {
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
var deactivateDialog by remember { mutableStateOf(false) } var deactivateDialog by remember { mutableStateOf(false) }
val profileOwner = context.isProfileOwner val privilege by myPrivilege.collectAsStateWithLifecycle()
val profileOwner = privilege.profile
MyScaffold(R.string.profile_owner, onNavigateUp) { MyScaffold(R.string.profile_owner, onNavigateUp) {
Text(stringResource(if(profileOwner) R.string.activated else R.string.deactivated), style = typography.titleLarge) Text(stringResource(if(profileOwner) R.string.activated else R.string.deactivated), style = typography.titleLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
@@ -432,7 +365,8 @@ fun DeviceOwnerScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
var deactivateDialog by remember { mutableStateOf(false) } var deactivateDialog by remember { mutableStateOf(false) }
val deviceOwner = context.isDeviceOwner val privilege by myPrivilege.collectAsStateWithLifecycle()
val deviceOwner = privilege.device
MyScaffold(R.string.device_owner, onNavigateUp) { MyScaffold(R.string.device_owner, onNavigateUp) {
Text(text = stringResource(if(deviceOwner) R.string.activated else R.string.deactivated), style = typography.titleLarge) Text(text = stringResource(if(deviceOwner) R.string.activated else R.string.deactivated), style = typography.titleLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
@@ -644,15 +578,15 @@ fun AddDelegatedAdminScreen(data: AddDelegatedAdmin, onNavigateUp: () -> Unit) {
fun DeviceInfoScreen(onNavigateUp: () -> Unit) { fun DeviceInfoScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val privilege by myPrivilege.collectAsStateWithLifecycle()
var dialog by remember { mutableIntStateOf(0) } var dialog by remember { mutableIntStateOf(0) }
MyScaffold(R.string.device_info, onNavigateUp, 0.dp) { MyScaffold(R.string.device_info, onNavigateUp, 0.dp) {
if(VERSION.SDK_INT>=34 && (context.isDeviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT>=34 && (privilege.device || privilege.org)) {
InfoItem(R.string.financed_device, dpm.isDeviceFinanced.yesOrNo) InfoItem(R.string.financed_device, dpm.isDeviceFinanced.yesOrNo)
} }
if(VERSION.SDK_INT >= 33) { if(VERSION.SDK_INT >= 33) {
val dpmRole = dpm.devicePolicyManagementRoleHolderPackage val dpmRole = dpm.devicePolicyManagementRoleHolderPackage
InfoItem(R.string.dpmrh, if(dpmRole == null) stringResource(R.string.none) else dpmRole) InfoItem(R.string.dpmrh, dpmRole ?: stringResource(R.string.none))
} }
val encryptionStatus = mutableMapOf( val encryptionStatus = mutableMapOf(
DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE to R.string.es_inactive, DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE to R.string.es_inactive,
@@ -670,7 +604,7 @@ fun DeviceInfoScreen(onNavigateUp: () -> Unit) {
} }
val adminList = dpm.activeAdmins val adminList = dpm.activeAdmins
if(adminList != null) { if(adminList != null) {
InfoItem(R.string.activated_device_admin, adminList.map { it.flattenToShortString() }.joinToString("\n")) InfoItem(R.string.activated_device_admin, adminList.joinToString("\n") { it.flattenToShortString() })
} }
} }
if(dialog != 0) AlertDialog( if(dialog != 0) AlertDialog(
@@ -766,6 +700,7 @@ fun SupportMessageScreen(onNavigateUp: () -> Unit) {
@Composable @Composable
fun TransferOwnershipScreen(onNavigateUp: () -> Unit) { fun TransferOwnershipScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val privilege by myPrivilege.collectAsStateWithLifecycle()
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
var input by remember { mutableStateOf("") } var input by remember { mutableStateOf("") }
val componentName = ComponentName.unflattenFromString(input) val componentName = ComponentName.unflattenFromString(input)
@@ -793,7 +728,7 @@ fun TransferOwnershipScreen(onNavigateUp: () -> Unit) {
text = { text = {
Text(stringResource( Text(stringResource(
R.string.transfer_ownership_warning, R.string.transfer_ownership_warning,
stringResource(if(context.isDeviceOwner) R.string.device_owner else R.string.profile_owner), stringResource(if(privilege.device) R.string.device_owner else R.string.profile_owner),
ComponentName.unflattenFromString(input)!!.packageName ComponentName.unflattenFromString(input)!!.packageName
)) ))
}, },
@@ -805,11 +740,12 @@ fun TransferOwnershipScreen(onNavigateUp: () -> Unit) {
try { try {
dpm.transferOwnership(receiver, componentName!!, null) dpm.transferOwnership(receiver, componentName!!, null)
context.showOperationResultToast(true) context.showOperationResultToast(true)
updatePrivilege(context)
dialog = false dialog = false
backToHomeStateFlow.value = true onNavigateUp()
} catch(e: Exception) { } catch(e: Exception) {
e.printStackTrace() e.printStackTrace()
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show() context.showOperationResultToast(false)
} }
}, },
colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error) colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error)
@@ -825,14 +761,3 @@ fun TransferOwnershipScreen(onNavigateUp: () -> Unit) {
onDismissRequest = { dialog = false } onDismissRequest = { dialog = false }
) )
} }
private fun activateDeviceAdmin(inputContext:Context,inputComponent:ComponentName) {
try {
val intent = Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN)
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, inputComponent)
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, inputContext.getString(R.string.activate_device_admin_here))
addDeviceAdmin.launch(intent)
} catch(_:ActivityNotFoundException) {
Toast.makeText(inputContext, R.string.unsupported, Toast.LENGTH_SHORT).show()
}
}

View File

@@ -19,7 +19,6 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -36,9 +35,12 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.IUserService import com.bintianqi.owndroid.IUserService
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.ui.MySmallTitleScaffold import com.bintianqi.owndroid.ui.MySmallTitleScaffold
import com.bintianqi.owndroid.updatePrivilege
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@@ -46,17 +48,13 @@ import rikka.shizuku.Shizuku
@Serializable object ShizukuScreen @Serializable object ShizukuScreen
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccountsViewer: (Accounts) -> Unit) { fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccountsViewer: (Accounts) -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val privilege by myPrivilege.collectAsStateWithLifecycle()
val receiver = context.getReceiver() val coroutine = rememberCoroutineScope()
val coScope = rememberCoroutineScope()
val outputTextScrollState = rememberScrollState() val outputTextScrollState = rememberScrollState()
var outputText by rememberSaveable { mutableStateOf("") } var outputText by rememberSaveable { mutableStateOf("") }
var showDeviceOwnerButton by remember { mutableStateOf(!context.isDeviceOwner) }
var showOrgProfileOwnerButton by remember { mutableStateOf(true) }
val binder = navArgs.getBinder("binder")!! val binder = navArgs.getBinder("binder")!!
var service by remember { mutableStateOf<IUserService?>(null) } var service by remember { mutableStateOf<IUserService?>(null) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
@@ -70,7 +68,7 @@ fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccount
Button( Button(
onClick = { onClick = {
coScope.launch{ coroutine.launch{
outputText = service!!.execute("dpm list-owners") outputText = service!!.execute("dpm list-owners")
outputTextScrollState.animateScrollTo(0) outputTextScrollState.animateScrollTo(0)
} }
@@ -81,7 +79,7 @@ fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccount
} }
Button( Button(
onClick = { onClick = {
coScope.launch{ coroutine.launch{
outputText = service!!.execute("pm list users") outputText = service!!.execute("pm list users")
outputTextScrollState.animateScrollTo(0) outputTextScrollState.animateScrollTo(0)
} }
@@ -100,7 +98,7 @@ fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccount
onNavigateToAccountsViewer(Accounts(accounts)) onNavigateToAccountsViewer(Accounts(accounts))
} catch(_: Exception) { } catch(_: Exception) {
outputText = service!!.execute("dumpsys account") outputText = service!!.execute("dumpsys account")
coScope.launch{ coroutine.launch{
outputTextScrollState.animateScrollTo(0) outputTextScrollState.animateScrollTo(0)
} }
} }
@@ -111,14 +109,14 @@ fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccount
} }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
AnimatedVisibility(showDeviceOwnerButton, modifier = Modifier.align(Alignment.CenterHorizontally)) { AnimatedVisibility(!privilege.device, modifier = Modifier.align(Alignment.CenterHorizontally)) {
Button( Button(
onClick = { onClick = {
coScope.launch{ coroutine.launch{
outputText = service!!.execute(context.getString(R.string.dpm_activate_do_command)) outputText = service!!.execute(context.getString(R.string.dpm_activate_do_command))
outputTextScrollState.animateScrollTo(0) outputTextScrollState.animateScrollTo(0)
delay(500) delay(500)
showDeviceOwnerButton = !context.isDeviceOwner updatePrivilege(context)
} }
} }
) { ) {
@@ -126,18 +124,17 @@ fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccount
} }
} }
if(VERSION.SDK_INT >= 30 && context.isProfileOwner && dpm.isManagedProfile(receiver) && !dpm.isOrganizationOwnedDeviceWithManagedProfile) { AnimatedVisibility(VERSION.SDK_INT >= 30 && privilege.work && !privilege.org) {
AnimatedVisibility(showOrgProfileOwnerButton) {
Button( Button(
onClick = { onClick = {
coScope.launch{ coroutine.launch{
val userID = Binder.getCallingUid() / 100000 val userID = Binder.getCallingUid() / 100000
outputText = service!!.execute( outputText = service!!.execute(
"dpm mark-profile-owner-on-organization-owned-device --user $userID com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver" "dpm mark-profile-owner-on-organization-owned-device --user $userID com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver"
) )
outputTextScrollState.animateScrollTo(0) outputTextScrollState.animateScrollTo(0)
delay(500) delay(500)
showOrgProfileOwnerButton = !dpm.isOrganizationOwnedDeviceWithManagedProfile updatePrivilege(context)
} }
}, },
modifier = Modifier.align(Alignment.CenterHorizontally) modifier = Modifier.align(Alignment.CenterHorizontally)
@@ -145,7 +142,6 @@ fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccount
Text(text = stringResource(R.string.activate_org_profile)) Text(text = stringResource(R.string.activate_org_profile))
} }
} }
}
SelectionContainer(modifier = Modifier.fillMaxWidth().horizontalScroll(outputTextScrollState)) { SelectionContainer(modifier = Modifier.fillMaxWidth().horizontalScroll(outputTextScrollState)) {
Text(text = outputText, softWrap = false, modifier = Modifier.padding(vertical = 9.dp, horizontal = 12.dp)) Text(text = outputText, softWrap = false, modifier = Modifier.padding(vertical = 9.dp, horizontal = 12.dp))

View File

@@ -116,6 +116,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.ChoosePackageContract import com.bintianqi.owndroid.ChoosePackageContract
import com.bintianqi.owndroid.HorizontalPadding import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.NotificationUtils import com.bintianqi.owndroid.NotificationUtils
@@ -123,6 +124,7 @@ import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.SharedPrefs import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.formatFileSize import com.bintianqi.owndroid.formatFileSize
import com.bintianqi.owndroid.humanReadableDate import com.bintianqi.owndroid.humanReadableDate
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.parseDate import com.bintianqi.owndroid.parseDate
import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
@@ -160,62 +162,54 @@ fun SystemManagerScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val sp = SharedPrefs(context) val sp = SharedPrefs(context)
val dhizuku = sp.dhizuku val privilege by myPrivilege.collectAsStateWithLifecycle()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
var dialog by remember { mutableIntStateOf(0) } var dialog by remember { mutableIntStateOf(0) }
MyScaffold(R.string.system, onNavigateUp, 0.dp) { MyScaffold(R.string.system, onNavigateUp, 0.dp) {
if(deviceOwner || profileOwner) {
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { onNavigate(SystemOptions) } FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { onNavigate(SystemOptions) }
}
FunctionItem(R.string.keyguard, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(Keyguard) } FunctionItem(R.string.keyguard, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(Keyguard) }
if(VERSION.SDK_INT >= 24 && deviceOwner && !dhizuku) if(VERSION.SDK_INT >= 24 && privilege.device && !privilege.dhizuku)
FunctionItem(R.string.hardware_monitor, icon = R.drawable.memory_fill0) { onNavigate(HardwareMonitor) } FunctionItem(R.string.hardware_monitor, icon = R.drawable.memory_fill0) { onNavigate(HardwareMonitor) }
if(VERSION.SDK_INT >= 24 && deviceOwner) { if(VERSION.SDK_INT >= 24 && privilege.device) {
FunctionItem(R.string.reboot, icon = R.drawable.restart_alt_fill0) { dialog = 1 } FunctionItem(R.string.reboot, icon = R.drawable.restart_alt_fill0) { dialog = 1 }
} }
if(deviceOwner && VERSION.SDK_INT >= 24 && (VERSION.SDK_INT < 28 || dpm.isAffiliatedUser)) { if(VERSION.SDK_INT >= 24 && privilege.device && (VERSION.SDK_INT < 28 || dpm.isAffiliatedUser)) {
FunctionItem(R.string.bug_report, icon = R.drawable.bug_report_fill0) { dialog = 2 } FunctionItem(R.string.bug_report, icon = R.drawable.bug_report_fill0) { dialog = 2 }
} }
if(VERSION.SDK_INT >= 28 && (deviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT >= 28 && (privilege.device || privilege.org)) {
FunctionItem(R.string.change_time, icon = R.drawable.schedule_fill0) { onNavigate(ChangeTime) } FunctionItem(R.string.change_time, icon = R.drawable.schedule_fill0) { onNavigate(ChangeTime) }
FunctionItem(R.string.change_timezone, icon = R.drawable.schedule_fill0) { onNavigate(ChangeTimeZone) } FunctionItem(R.string.change_timezone, icon = R.drawable.schedule_fill0) { onNavigate(ChangeTimeZone) }
} }
/*if(VERSION.SDK_INT >= 28 && (deviceOwner || profileOwner)) /*if(VERSION.SDK_INT >= 28 && (deviceOwner || profileOwner))
FunctionItem(R.string.key_pairs, icon = R.drawable.key_vertical_fill0) { navCtrl.navigate("KeyPairs") }*/ FunctionItem(R.string.key_pairs, icon = R.drawable.key_vertical_fill0) { navCtrl.navigate("KeyPairs") }*/
if(VERSION.SDK_INT >= 35 && (deviceOwner || (profileOwner && dpm.isAffiliatedUser))) if(VERSION.SDK_INT >= 35 && (privilege.device || (privilege.profile && privilege.affiliated)))
FunctionItem(R.string.content_protection_policy, icon = R.drawable.search_fill0) { onNavigate(ContentProtectionPolicy) } FunctionItem(R.string.content_protection_policy, icon = R.drawable.search_fill0) { onNavigate(ContentProtectionPolicy) }
if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) { if(VERSION.SDK_INT >= 23) {
FunctionItem(R.string.permission_policy, icon = R.drawable.key_fill0) { onNavigate(PermissionPolicy) } FunctionItem(R.string.permission_policy, icon = R.drawable.key_fill0) { onNavigate(PermissionPolicy) }
} }
if(VERSION.SDK_INT >= 34 && deviceOwner) { if(VERSION.SDK_INT >= 34 && privilege.device) {
FunctionItem(R.string.mte_policy, icon = R.drawable.memory_fill0) { onNavigate(MtePolicy) } FunctionItem(R.string.mte_policy, icon = R.drawable.memory_fill0) { onNavigate(MtePolicy) }
} }
if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) { if(VERSION.SDK_INT >= 31) {
FunctionItem(R.string.nearby_streaming_policy, icon = R.drawable.share_fill0) { onNavigate(NearbyStreamingPolicy) } FunctionItem(R.string.nearby_streaming_policy, icon = R.drawable.share_fill0) { onNavigate(NearbyStreamingPolicy) }
} }
if(VERSION.SDK_INT >= 28 && deviceOwner) { if(VERSION.SDK_INT >= 28 && privilege.device) {
FunctionItem(R.string.lock_task_mode, icon = R.drawable.lock_fill0) { onNavigate(LockTaskMode) } FunctionItem(R.string.lock_task_mode, icon = R.drawable.lock_fill0) { onNavigate(LockTaskMode) }
} }
if(deviceOwner || profileOwner) {
FunctionItem(R.string.ca_cert, icon = R.drawable.license_fill0) { onNavigate(CaCert) } FunctionItem(R.string.ca_cert, icon = R.drawable.license_fill0) { onNavigate(CaCert) }
} if(VERSION.SDK_INT >= 26 && !privilege.dhizuku && (privilege.device || privilege.org)) {
if(VERSION.SDK_INT >= 26 && !dhizuku && (deviceOwner || dpm.isOrgProfile(receiver))) {
FunctionItem(R.string.security_logging, icon = R.drawable.description_fill0) { onNavigate(SecurityLogging) } FunctionItem(R.string.security_logging, icon = R.drawable.description_fill0) { onNavigate(SecurityLogging) }
} }
if(deviceOwner || profileOwner) {
FunctionItem(R.string.disable_account_management, icon = R.drawable.account_circle_fill0) { onNavigate(DisableAccountManagement) } FunctionItem(R.string.disable_account_management, icon = R.drawable.account_circle_fill0) { onNavigate(DisableAccountManagement) }
} if(VERSION.SDK_INT >= 23 && (privilege.device || privilege.org)) {
if(VERSION.SDK_INT >= 23 && (deviceOwner || dpm.isOrgProfile(receiver))) {
FunctionItem(R.string.system_update_policy, icon = R.drawable.system_update_fill0) { onNavigate(SetSystemUpdatePolicy) } FunctionItem(R.string.system_update_policy, icon = R.drawable.system_update_fill0) { onNavigate(SetSystemUpdatePolicy) }
} }
if(VERSION.SDK_INT >= 29 && (deviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT >= 29 && (privilege.device || privilege.org)) {
FunctionItem(R.string.install_system_update, icon = R.drawable.system_update_fill0) { onNavigate(InstallSystemUpdate) } FunctionItem(R.string.install_system_update, icon = R.drawable.system_update_fill0) { onNavigate(InstallSystemUpdate) }
} }
if(VERSION.SDK_INT >= 30 && (deviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT >= 30 && (privilege.device || privilege.org)) {
FunctionItem(R.string.frp_policy, icon = R.drawable.device_reset_fill0) { onNavigate(FrpPolicy) } FunctionItem(R.string.frp_policy, icon = R.drawable.device_reset_fill0) { onNavigate(FrpPolicy) }
} }
if(sp.displayDangerousFeatures && context.isDeviceAdmin && !(VERSION.SDK_INT >= 24 && profileOwner && dpm.isManagedProfile(receiver))) { if(sp.displayDangerousFeatures && !privilege.work) {
FunctionItem(R.string.wipe_data, icon = R.drawable.device_reset_fill0) { onNavigate(WipeData) } FunctionItem(R.string.wipe_data, icon = R.drawable.device_reset_fill0) { onNavigate(WipeData) }
} }
} }
@@ -253,27 +247,21 @@ fun SystemOptionsScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner val privilege by myPrivilege.collectAsStateWithLifecycle()
val profileOwner = context.isProfileOwner
val um = context.getSystemService(Context.USER_SERVICE) as UserManager
var dialog by remember { mutableIntStateOf(0) } var dialog by remember { mutableIntStateOf(0) }
MyScaffold(R.string.options, onNavigateUp, 0.dp) { MyScaffold(R.string.options, onNavigateUp, 0.dp) {
if(deviceOwner || profileOwner) {
SwitchItem(R.string.disable_cam, icon = R.drawable.photo_camera_fill0, SwitchItem(R.string.disable_cam, icon = R.drawable.photo_camera_fill0,
getState = { dpm.getCameraDisabled(null) }, onCheckedChange = { dpm.setCameraDisabled(receiver,it) } getState = { dpm.getCameraDisabled(null) }, onCheckedChange = { dpm.setCameraDisabled(receiver,it) }
) )
}
if(deviceOwner || profileOwner) {
SwitchItem(R.string.disable_screen_capture, icon = R.drawable.screenshot_fill0, SwitchItem(R.string.disable_screen_capture, icon = R.drawable.screenshot_fill0,
getState = { dpm.getScreenCaptureDisabled(null) }, onCheckedChange = { dpm.setScreenCaptureDisabled(receiver,it) } getState = { dpm.getScreenCaptureDisabled(null) }, onCheckedChange = { dpm.setScreenCaptureDisabled(receiver,it) }
) )
} if(VERSION.SDK_INT >= 34 && (privilege.device || (privilege.profile && privilege.affiliated))) {
if(VERSION.SDK_INT >= 34 && (deviceOwner || (profileOwner && dpm.isAffiliatedUser))) {
SwitchItem(R.string.disable_status_bar, icon = R.drawable.notifications_fill0, SwitchItem(R.string.disable_status_bar, icon = R.drawable.notifications_fill0,
getState = { dpm.isStatusBarDisabled}, onCheckedChange = { dpm.setStatusBarDisabled(receiver,it) } getState = { dpm.isStatusBarDisabled}, onCheckedChange = { dpm.setStatusBarDisabled(receiver,it) }
) )
} }
if(deviceOwner || (VERSION.SDK_INT >= 23 && profileOwner && um.isSystemUser) || dpm.isOrgProfile(receiver)) { if(privilege.device || privilege.org) {
if(VERSION.SDK_INT >= 30) { if(VERSION.SDK_INT >= 30) {
SwitchItem(R.string.auto_time, icon = R.drawable.schedule_fill0, SwitchItem(R.string.auto_time, icon = R.drawable.schedule_fill0,
getState = { dpm.getAutoTimeEnabled(receiver) }, onCheckedChange = { dpm.setAutoTimeEnabled(receiver,it) } getState = { dpm.getAutoTimeEnabled(receiver) }, onCheckedChange = { dpm.setAutoTimeEnabled(receiver,it) }
@@ -286,30 +274,28 @@ fun SystemOptionsScreen(onNavigateUp: () -> Unit) {
getState = { dpm.autoTimeRequired }, onCheckedChange = { dpm.setAutoTimeRequired(receiver,it) }, padding = false) getState = { dpm.autoTimeRequired }, onCheckedChange = { dpm.setAutoTimeRequired(receiver,it) }, padding = false)
} }
} }
if(deviceOwner || profileOwner) {
SwitchItem(R.string.master_mute, icon = R.drawable.volume_off_fill0, SwitchItem(R.string.master_mute, icon = R.drawable.volume_off_fill0,
getState = { dpm.isMasterVolumeMuted(receiver) }, onCheckedChange = { dpm.setMasterVolumeMuted(receiver,it) } getState = { dpm.isMasterVolumeMuted(receiver) }, onCheckedChange = { dpm.setMasterVolumeMuted(receiver,it) }
) )
} if(VERSION.SDK_INT >= 26) {
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) {
SwitchItem(R.string.backup_service, icon = R.drawable.backup_fill0, SwitchItem(R.string.backup_service, icon = R.drawable.backup_fill0,
getState = { dpm.isBackupServiceEnabled(receiver) }, onCheckedChange = { dpm.setBackupServiceEnabled(receiver,it) }, getState = { dpm.isBackupServiceEnabled(receiver) }, onCheckedChange = { dpm.setBackupServiceEnabled(receiver,it) },
onClickBlank = { dialog = 1 } onClickBlank = { dialog = 1 }
) )
} }
if(VERSION.SDK_INT >= 24 && profileOwner && dpm.isManagedProfile(receiver)) { if(VERSION.SDK_INT >= 24 && privilege.work) {
SwitchItem(R.string.disable_bt_contact_share, icon = R.drawable.account_circle_fill0, SwitchItem(R.string.disable_bt_contact_share, icon = R.drawable.account_circle_fill0,
getState = { dpm.getBluetoothContactSharingDisabled(receiver) }, getState = { dpm.getBluetoothContactSharingDisabled(receiver) },
onCheckedChange = { dpm.setBluetoothContactSharingDisabled(receiver,it) } onCheckedChange = { dpm.setBluetoothContactSharingDisabled(receiver,it) }
) )
} }
if(VERSION.SDK_INT >= 30 && deviceOwner) { if(VERSION.SDK_INT >= 30 && privilege.device) {
SwitchItem(R.string.common_criteria_mode , icon =R.drawable.security_fill0, SwitchItem(R.string.common_criteria_mode , icon =R.drawable.security_fill0,
getState = { dpm.isCommonCriteriaModeEnabled(receiver) }, onCheckedChange = { dpm.setCommonCriteriaModeEnabled(receiver,it) }, getState = { dpm.isCommonCriteriaModeEnabled(receiver) }, onCheckedChange = { dpm.setCommonCriteriaModeEnabled(receiver,it) },
onClickBlank = { dialog = 2 } onClickBlank = { dialog = 2 }
) )
} }
if(VERSION.SDK_INT >= 31 && (deviceOwner || dpm.isOrgProfile(receiver)) && dpm.canUsbDataSignalingBeDisabled()) { if(VERSION.SDK_INT >= 31 && (privilege.device || privilege.org) && dpm.canUsbDataSignalingBeDisabled()) {
SwitchItem( SwitchItem(
R.string.disable_usb_signal, icon = R.drawable.usb_fill0, getState = { !dpm.isUsbDataSignalingEnabled }, R.string.disable_usb_signal, icon = R.drawable.usb_fill0, getState = { !dpm.isUsbDataSignalingEnabled },
onCheckedChange = { dpm.isUsbDataSignalingEnabled = !it }, onCheckedChange = { dpm.isUsbDataSignalingEnabled = !it },
@@ -340,24 +326,21 @@ fun KeyguardScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner val privilege by myPrivilege.collectAsStateWithLifecycle()
val profileOwner = context.isProfileOwner
MyScaffold(R.string.keyguard, onNavigateUp) { MyScaffold(R.string.keyguard, onNavigateUp) {
if(VERSION.SDK_INT >= 23) { if(VERSION.SDK_INT >= 23 && (privilege.device || (VERSION.SDK_INT >= 28 && privilege.profile && privilege.affiliated))) {
Row( Row(
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Button( Button(
onClick = { context.showOperationResultToast(dpm.setKeyguardDisabled(receiver, true)) }, onClick = { context.showOperationResultToast(dpm.setKeyguardDisabled(receiver, true)) },
enabled = deviceOwner || (VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser),
modifier = Modifier.fillMaxWidth(0.49F) modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
Text(stringResource(R.string.disable)) Text(stringResource(R.string.disable))
} }
Button( Button(
onClick = { context.showOperationResultToast(dpm.setKeyguardDisabled(receiver, false)) }, onClick = { context.showOperationResultToast(dpm.setKeyguardDisabled(receiver, false)) },
enabled = deviceOwner || (VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser),
modifier = Modifier.fillMaxWidth(0.96F) modifier = Modifier.fillMaxWidth(0.96F)
) { ) {
Text(stringResource(R.string.enable)) Text(stringResource(R.string.enable))
@@ -369,7 +352,7 @@ fun KeyguardScreen(onNavigateUp: () -> Unit) {
if(VERSION.SDK_INT >= 23) Text(text = stringResource(R.string.lock_now), style = typography.headlineLarge) if(VERSION.SDK_INT >= 23) Text(text = stringResource(R.string.lock_now), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 2.dp)) Spacer(Modifier.padding(vertical = 2.dp))
var flag by remember { mutableIntStateOf(0) } var flag by remember { mutableIntStateOf(0) }
if(VERSION.SDK_INT >= 26 && profileOwner && dpm.isManagedProfile(receiver)) { if(VERSION.SDK_INT >= 26 && privilege.work) {
CheckBoxItem( CheckBoxItem(
R.string.evict_credential_encryption_key, R.string.evict_credential_encryption_key,
flag and FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY != 0 flag and FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY != 0
@@ -380,12 +363,11 @@ fun KeyguardScreen(onNavigateUp: () -> Unit) {
onClick = { onClick = {
if(VERSION.SDK_INT >= 26) dpm.lockNow(flag) else dpm.lockNow() if(VERSION.SDK_INT >= 26) dpm.lockNow(flag) else dpm.lockNow()
}, },
enabled = context.isDeviceAdmin,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text(stringResource(R.string.lock_now)) Text(stringResource(R.string.lock_now))
} }
if(VERSION.SDK_INT >= 26 && profileOwner && dpm.isManagedProfile(receiver)) { if(VERSION.SDK_INT >= 26 && privilege.work) {
Notes(R.string.info_evict_credential_encryption_key) Notes(R.string.info_evict_credential_encryption_key)
} }
} }
@@ -393,7 +375,6 @@ fun KeyguardScreen(onNavigateUp: () -> Unit) {
@Serializable object HardwareMonitor @Serializable object HardwareMonitor
@OptIn(ExperimentalMaterial3Api::class)
@RequiresApi(24) @RequiresApi(24)
@Composable @Composable
fun HardwareMonitorScreen(onNavigateUp: () -> Unit) { fun HardwareMonitorScreen(onNavigateUp: () -> Unit) {
@@ -1332,7 +1313,7 @@ fun CaCertScreen(onNavigateUp: () -> Unit) {
text = { text = {
if(dialog == 3) Text(stringResource(R.string.uninstall_all_user_ca_cert)) if(dialog == 3) Text(stringResource(R.string.uninstall_all_user_ca_cert))
else { else {
var text = "" var text: String
val sha256 = MessageDigest.getInstance("SHA-256").digest(caCertByteArray).toHexString() val sha256 = MessageDigest.getInstance("SHA-256").digest(caCertByteArray).toHexString()
try { try {
val cf = CertificateFactory.getInstance("X.509") val cf = CertificateFactory.getInstance("X.509")
@@ -1634,6 +1615,7 @@ fun WipeDataScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
val dpm = context.getDPM() val dpm = context.getDPM()
val privilege by myPrivilege.collectAsStateWithLifecycle()
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
var flag by remember { mutableIntStateOf(0) } var flag by remember { mutableIntStateOf(0) }
var warning by remember { mutableStateOf(false) } var warning by remember { mutableStateOf(false) }
@@ -1642,7 +1624,7 @@ fun WipeDataScreen(onNavigateUp: () -> Unit) {
var reason by remember { mutableStateOf("") } var reason by remember { mutableStateOf("") }
MyScaffold(R.string.wipe_data, onNavigateUp) { MyScaffold(R.string.wipe_data, onNavigateUp) {
CheckBoxItem(R.string.wipe_external_storage, flag and WIPE_EXTERNAL_STORAGE != 0) { flag = flag xor WIPE_EXTERNAL_STORAGE } CheckBoxItem(R.string.wipe_external_storage, flag and WIPE_EXTERNAL_STORAGE != 0) { flag = flag xor WIPE_EXTERNAL_STORAGE }
if(VERSION.SDK_INT >= 22 && context.isDeviceOwner) CheckBoxItem( if(VERSION.SDK_INT >= 22 && privilege.device) CheckBoxItem(
R.string.wipe_reset_protection_data, flag and WIPE_RESET_PROTECTION_DATA != 0) { flag = flag xor WIPE_RESET_PROTECTION_DATA } R.string.wipe_reset_protection_data, flag and WIPE_RESET_PROTECTION_DATA != 0) { flag = flag xor WIPE_RESET_PROTECTION_DATA }
if(VERSION.SDK_INT >= 28) CheckBoxItem(R.string.wipe_euicc, flag and WIPE_EUICC != 0) { flag = flag xor WIPE_EUICC } if(VERSION.SDK_INT >= 28) CheckBoxItem(R.string.wipe_euicc, flag and WIPE_EUICC != 0) { flag = flag xor WIPE_EUICC }
if(VERSION.SDK_INT >= 29) CheckBoxItem(R.string.wipe_silently, silent) { silent = it } if(VERSION.SDK_INT >= 29) CheckBoxItem(R.string.wipe_silently, silent) { silent = it }
@@ -1667,7 +1649,7 @@ fun WipeDataScreen(onNavigateUp: () -> Unit) {
Text("WipeData") Text("WipeData")
} }
} }
if (VERSION.SDK_INT >= 34 && context.isDeviceOwner) { if (VERSION.SDK_INT >= 34 && privilege.device) {
Button( Button(
onClick = { onClick = {
focusMgr.clearFocus() focusMgr.clearFocus()

View File

@@ -10,11 +10,13 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.SwitchItem
@@ -33,14 +35,12 @@ data class Restriction(
@RequiresApi(24) @RequiresApi(24)
@Composable @Composable
fun UserRestrictionScreen(onNavigateUp: () -> Unit, onNavigate: (Int, List<Restriction>) -> Unit) { fun UserRestrictionScreen(onNavigateUp: () -> Unit, onNavigate: (Int, List<Restriction>) -> Unit) {
val context = LocalContext.current val privilege by myPrivilege.collectAsStateWithLifecycle()
val dpm = context.getDPM()
val receiver = context.getReceiver()
MyScaffold(R.string.user_restriction, onNavigateUp, 0.dp) { MyScaffold(R.string.user_restriction, onNavigateUp, 0.dp) {
Spacer(Modifier.padding(vertical = 2.dp)) Spacer(Modifier.padding(vertical = 2.dp))
Text(text = stringResource(R.string.switch_to_disable_feature), modifier = Modifier.padding(start = 16.dp)) Text(text = stringResource(R.string.switch_to_disable_feature), modifier = Modifier.padding(start = 16.dp))
if(context.isProfileOwner) { Text(text = stringResource(R.string.profile_owner_is_restricted), modifier = Modifier.padding(start = 16.dp)) } if(privilege.profile) { Text(text = stringResource(R.string.profile_owner_is_restricted), modifier = Modifier.padding(start = 16.dp)) }
if(context.isProfileOwner && dpm.isManagedProfile(receiver)) { if(privilege.work) {
Text(text = stringResource(R.string.some_features_invalid_in_work_profile), modifier = Modifier.padding(start = 16.dp)) Text(text = stringResource(R.string.some_features_invalid_in_work_profile), modifier = Modifier.padding(start = 16.dp))
} }
Spacer(Modifier.padding(vertical = 2.dp)) Spacer(Modifier.padding(vertical = 2.dp))

View File

@@ -64,8 +64,10 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.HorizontalPadding import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.parseTimestamp import com.bintianqi.owndroid.parseTimestamp
import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem
@@ -89,28 +91,25 @@ fun UsersScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner val privilege by myPrivilege.collectAsStateWithLifecycle()
val profileOwner = context.isProfileOwner
var dialog by remember { mutableIntStateOf(0) } var dialog by remember { mutableIntStateOf(0) }
MyScaffold(R.string.users, onNavigateUp, 0.dp) { MyScaffold(R.string.users, onNavigateUp, 0.dp) {
if(VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser) { if(VERSION.SDK_INT >= 28 && privilege.profile && privilege.affiliated) {
FunctionItem(R.string.logout, icon = R.drawable.logout_fill0) { dialog = 2 } FunctionItem(R.string.logout, icon = R.drawable.logout_fill0) { dialog = 2 }
} }
FunctionItem(R.string.user_info, icon = R.drawable.person_fill0) { onNavigate(UserInfo) } FunctionItem(R.string.user_info, icon = R.drawable.person_fill0) { onNavigate(UserInfo) }
if(deviceOwner && VERSION.SDK_INT >= 28) { if(VERSION.SDK_INT >= 28 && privilege.device) {
FunctionItem(R.string.secondary_users, icon = R.drawable.list_fill0) { dialog = 1 } FunctionItem(R.string.secondary_users, icon = R.drawable.list_fill0) { dialog = 1 }
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { onNavigate(UsersOptions) } FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { onNavigate(UsersOptions) }
} }
if(deviceOwner) { if(privilege.device) {
FunctionItem(R.string.user_operation, icon = R.drawable.sync_alt_fill0) { onNavigate(UserOperation) } FunctionItem(R.string.user_operation, icon = R.drawable.sync_alt_fill0) { onNavigate(UserOperation) }
} }
if(VERSION.SDK_INT >= 24 && deviceOwner) { if(VERSION.SDK_INT >= 24 && privilege.device) {
FunctionItem(R.string.create_user, icon = R.drawable.person_add_fill0) { onNavigate(CreateUser) } FunctionItem(R.string.create_user, icon = R.drawable.person_add_fill0) { onNavigate(CreateUser) }
} }
if(deviceOwner || profileOwner) {
FunctionItem(R.string.change_username, icon = R.drawable.edit_fill0) { onNavigate(ChangeUsername) } FunctionItem(R.string.change_username, icon = R.drawable.edit_fill0) { onNavigate(ChangeUsername) }
} if(VERSION.SDK_INT >= 23) {
if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) {
var changeUserIconDialog by remember { mutableStateOf(false) } var changeUserIconDialog by remember { mutableStateOf(false) }
var bitmap: Bitmap? by remember { mutableStateOf(null) } var bitmap: Bitmap? by remember { mutableStateOf(null) }
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
@@ -123,12 +122,12 @@ fun UsersScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
Toast.makeText(context, R.string.select_an_image, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.select_an_image, Toast.LENGTH_SHORT).show()
launcher.launch("image/*") launcher.launch("image/*")
} }
if(changeUserIconDialog == true) ChangeUserIconDialog(bitmap!!) { changeUserIconDialog = false } if(changeUserIconDialog) ChangeUserIconDialog(bitmap!!) { changeUserIconDialog = false }
} }
if(VERSION.SDK_INT >= 28 && deviceOwner) { if(VERSION.SDK_INT >= 28 && privilege.device) {
FunctionItem(R.string.user_session_msg, icon = R.drawable.notifications_fill0) { onNavigate(UserSessionMessage) } FunctionItem(R.string.user_session_msg, icon = R.drawable.notifications_fill0) { onNavigate(UserSessionMessage) }
} }
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) { if(VERSION.SDK_INT >= 26) {
FunctionItem(R.string.affiliation_id, icon = R.drawable.id_card_fill0) { onNavigate(AffiliationId) } FunctionItem(R.string.affiliation_id, icon = R.drawable.id_card_fill0) { onNavigate(AffiliationId) }
} }
} }
@@ -190,6 +189,7 @@ fun UserInfoScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val privilege by myPrivilege.collectAsStateWithLifecycle()
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
val user = Process.myUserHandle() val user = Process.myUserHandle()
var infoDialog by remember { mutableIntStateOf(0) } var infoDialog by remember { mutableIntStateOf(0) }
@@ -205,10 +205,8 @@ fun UserInfoScreen(onNavigateUp: () -> Unit) {
} }
if (VERSION.SDK_INT >= 28) { if (VERSION.SDK_INT >= 28) {
InfoItem(R.string.logout_enabled, dpm.isLogoutEnabled.yesOrNo) InfoItem(R.string.logout_enabled, dpm.isLogoutEnabled.yesOrNo)
if(context.isDeviceOwner || context.isProfileOwner) {
InfoItem(R.string.ephemeral_user, dpm.isEphemeralUser(receiver).yesOrNo) InfoItem(R.string.ephemeral_user, dpm.isEphemeralUser(receiver).yesOrNo)
} InfoItem(R.string.affiliated_user, privilege.affiliated.yesOrNo)
InfoItem(R.string.affiliated_user, dpm.isAffiliatedUser.yesOrNo)
} }
InfoItem(R.string.user_id, (Binder.getCallingUid() / 100000).toString()) InfoItem(R.string.user_id, (Binder.getCallingUid() / 100000).toString())
InfoItem(R.string.user_serial_number, userManager.getSerialNumberForUser(Process.myUserHandle()).toString()) InfoItem(R.string.user_serial_number, userManager.getSerialNumberForUser(Process.myUserHandle()).toString())
@@ -447,7 +445,7 @@ fun AffiliationIdScreen(onNavigateUp: () -> Unit) {
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { onClick = {
list.removeAll(listOf("")) list.removeAll(setOf(""))
dpm.setAffiliationIds(receiver, list.toSet()) dpm.setAffiliationIds(receiver, list.toSet())
context.showOperationResultToast(true) context.showOperationResultToast(true)
refreshIds() refreshIds()

View File

@@ -53,7 +53,9 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.CopyTextButton import com.bintianqi.owndroid.ui.CopyTextButton
@@ -69,28 +71,18 @@ import kotlinx.serialization.Serializable
@Composable @Composable
fun WorkProfileScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) { fun WorkProfileScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
val context = LocalContext.current val privilege by myPrivilege.collectAsStateWithLifecycle()
val dpm = context.getDPM()
val receiver = context.getReceiver()
val profileOwner = context.isProfileOwner
MyScaffold(R.string.work_profile, onNavigateUp, 0.dp) { MyScaffold(R.string.work_profile, onNavigateUp, 0.dp) {
if(VERSION.SDK_INT >= 30 && profileOwner && dpm.isManagedProfile(receiver)) { if(VERSION.SDK_INT >= 30) {
FunctionItem(R.string.org_owned_work_profile, icon = R.drawable.corporate_fare_fill0) { onNavigate(OrganizationOwnedProfile) } FunctionItem(R.string.org_owned_work_profile, icon = R.drawable.corporate_fare_fill0) { onNavigate(OrganizationOwnedProfile) }
} }
if(VERSION.SDK_INT < 24 || dpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)) { if(privilege.org) {
FunctionItem(R.string.create_work_profile, icon = R.drawable.work_fill0) { onNavigate(CreateWorkProfile) }
}
if(dpm.isOrgProfile(receiver)) {
FunctionItem(R.string.suspend_personal_app, icon = R.drawable.block_fill0) { onNavigate(SuspendPersonalApp) } FunctionItem(R.string.suspend_personal_app, icon = R.drawable.block_fill0) { onNavigate(SuspendPersonalApp) }
} }
if(profileOwner && (VERSION.SDK_INT < 24 || dpm.isManagedProfile(receiver))) {
FunctionItem(R.string.intent_filter, icon = R.drawable.filter_alt_fill0) { onNavigate(CrossProfileIntentFilter) } FunctionItem(R.string.intent_filter, icon = R.drawable.filter_alt_fill0) { onNavigate(CrossProfileIntentFilter) }
}
if(profileOwner && (VERSION.SDK_INT < 24 || dpm.isManagedProfile(receiver))) {
FunctionItem(R.string.delete_work_profile, icon = R.drawable.delete_forever_fill0) { onNavigate(DeleteWorkProfile) } FunctionItem(R.string.delete_work_profile, icon = R.drawable.delete_forever_fill0) { onNavigate(DeleteWorkProfile) }
} }
} }
}
@Serializable object CreateWorkProfile @Serializable object CreateWorkProfile

View File

@@ -73,8 +73,6 @@
<!--Разрешения--> <!--Разрешения-->
<string name="click_to_activate">Нажмите для активации</string> <string name="click_to_activate">Нажмите для активации</string>
<string name="device_admin">Администратор устройства</string>
<string name="activate_jump" tools:ignore="TypographyEllipsis">Активировать...</string>
<string name="profile_owner">Владелец профиля</string> <string name="profile_owner">Владелец профиля</string>
<string name="device_owner">Владелец устройства</string> <string name="device_owner">Владелец устройства</string>
<string name="delegated_admins">Делегированные администраторы</string> <string name="delegated_admins">Делегированные администраторы</string>
@@ -116,9 +114,7 @@
<string name="activate_device_admin_here">Активируйте администратора устройства здесь.</string> <string name="activate_device_admin_here">Активируйте администратора устройства здесь.</string>
<!--Приемник--> <!--Приемник-->
<string name="onEnabled">OwnDroid: Включено</string> <string name="create_work_profile_success">Рабочий профиль успешно создан</string>
<string name="onDisabled">OwnDroid: Отключено</string>
<string name="create_work_profile_success">OwnDroid: Рабочий профиль успешно создан</string>
<!--Dhizuku--> <!--Dhizuku-->
<string name="failed_to_init_dhizuku">Не удалось инициализировать Dhizuku</string> <string name="failed_to_init_dhizuku">Не удалось инициализировать Dhizuku</string>

View File

@@ -1,7 +1,5 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<!--Global--> <!--Global-->
<string name="app_name" translatable="false">OwnDroid</string>
<string name="place_holder" translatable="false"/>
<string name="disabled">Devre Dışı</string> <string name="disabled">Devre Dışı</string>
<string name="enabled">Etkin</string> <string name="enabled">Etkin</string>
<string name="disable">Devre Dışı Bırak</string> <string name="disable">Devre Dışı Bırak</string>
@@ -64,7 +62,6 @@
<string name="alias">Takma Ad</string> <string name="alias">Takma Ad</string>
<string name="unknown_error">Bilinmeyen Hata</string> <string name="unknown_error">Bilinmeyen Hata</string>
<string name="permission_denied">İzin Reddedildi</string> <string name="permission_denied">İzin Reddedildi</string>
<string name="api" translatable="false">API</string>
<string name="error">Hata</string> <string name="error">Hata</string>
<string name="status">Durum</string> <string name="status">Durum</string>
<string name="edit">Düzenle</string> <string name="edit">Düzenle</string>
@@ -75,8 +72,6 @@
<!--Permissions--> <!--Permissions-->
<string name="click_to_activate">Etkinleştirmek için Tıklayın</string> <string name="click_to_activate">Etkinleştirmek için Tıklayın</string>
<string name="device_admin">Cihaz Yöneticisi</string>
<string name="activate_jump" tools:ignore="TypographyEllipsis">Etkinleştir...</string>
<string name="profile_owner">Profil Sahibi</string> <string name="profile_owner">Profil Sahibi</string>
<string name="device_owner">Cihaz Sahibi</string> <string name="device_owner">Cihaz Sahibi</string>
<string name="delegated_admins">Yetkilendirilmiş Yöneticiler</string> <string name="delegated_admins">Yetkilendirilmiş Yöneticiler</string>
@@ -91,9 +86,6 @@
<string name="add_delegated_admin">Yetkilendirilmiş Yönetici Ekle</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="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="reset_device_policy">Cihaz Politikasını Sıfırla</string>
<string name="activate_device_admin_command" translatable="false">dpm set-active-admin com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver</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">Cihaz Bilgisi</string> <string name="device_info">Cihaz Bilgisi</string>
<string name="support_device_id_attestation">Cihaz Kimliği Doğrulamasını Destekler</string> <string name="support_device_id_attestation">Cihaz Kimliği Doğrulamasını Destekler</string>
<string name="support_unique_device_attestation">Benzersiz Cihaz Doğrulamasını Destekler</string> <string name="support_unique_device_attestation">Benzersiz Cihaz Doğrulamasını Destekler</string>
@@ -121,22 +113,17 @@
<string name="activate_device_admin_here">Cihaz Yöneticisini Burada Etkinleştir.</string> <string name="activate_device_admin_here">Cihaz Yöneticisini Burada Etkinleştir.</string>
<!--Receiver--> <!--Receiver-->
<string name="onEnabled">OwnDroid: Etkin</string> <string name="create_work_profile_success">İş Profili Başarıyla Oluşturuldu</string>
<string name="onDisabled">OwnDroid: Devre Dışı</string>
<string name="create_work_profile_success">OwnDroid: İş Profili Başarıyla Oluşturuldu</string>
<!--Dhizuku--> <!--Dhizuku-->
<string name="dhizuku" translatable="false">Dhizuku</string>
<string name="failed_to_init_dhizuku">Dhizuku Başlatılamadı</string> <string name="failed_to_init_dhizuku">Dhizuku Başlatılamadı</string>
<string name="dhizuku_permission_not_granted">Dhizuku İzni Verilmedi</string> <string name="dhizuku_permission_not_granted">Dhizuku İzni Verilmedi</string>
<string name="dhizuku_mode_disabled">Dhizuku Modu Devre Dışı</string> <string name="dhizuku_mode_disabled">Dhizuku Modu Devre Dışı</string>
<!--Shizuku--> <!--Shizuku-->
<string name="shizuku" translatable="false">Shizuku</string>
<string name="list_owners">Sahip Listesi</string> <string name="list_owners">Sahip Listesi</string>
<string name="list_users">Kullanıcı Listesi</string> <string name="list_users">Kullanıcı Listesi</string>
<string name="list_accounts">Hesap Listesi</string> <string name="list_accounts">Hesap Listesi</string>
<string name="shizuku_not_started">Shizuku Başlatılmadı.</string> <string name="shizuku_not_started">Shizuku Başlatılmadı.</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">Cihaz Sahibini Etkinleştir</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="activate_org_profile">Kuruluşa Ait İş Profilini Etkinleştir</string>
<string name="accounts">Hesaplar</string> <string name="accounts">Hesaplar</string>
@@ -246,7 +233,6 @@
<!--Network--> <!--Network-->
<string name="network"></string> <string name="network"></string>
<string name="wifi_mac_address">Wi-Fi MAC Adresi</string> <string name="wifi_mac_address">Wi-Fi MAC Adresi</string>
<string name="wifi" translatable="false">Wi-Fi</string>
<string name="disconnect">Bağlantıyı Kes</string> <string name="disconnect">Bağlantıyı Kes</string>
<string name="reconnect">Yeniden Bağlan</string> <string name="reconnect">Yeniden Bağlan</string>
<string name="saved_networks">Kayıtlı Ağlar</string> <string name="saved_networks">Kayıtlı Ağlar</string>
@@ -277,13 +263,11 @@
<string name="target">Hedef</string> <string name="target">Hedef</string>
<string name="device">Cihaz</string> <string name="device">Cihaz</string>
<string name="user">Kullanıcı</string> <string name="user">Kullanıcı</string>
<string name="uid" translatable="false">UID</string>
<string name="uid_tag">UID Etiketi</string> <string name="uid_tag">UID Etiketi</string>
<string name="uid_tag_state">UID Etiket Durumu</string> <string name="uid_tag_state">UID Etiket Durumu</string>
<string name="network_type">Ağ Türü</string> <string name="network_type">Ağ Türü</string>
<string name="mobile">Mobil</string> <string name="mobile">Mobil</string>
<string name="ethernet">Ethernet</string> <string name="ethernet">Ethernet</string>
<string name="vpn" translatable="false">VPN</string>
<string name="subscriber_id">Abone Kimliği</string> <string name="subscriber_id">Abone Kimliği</string>
<string name="all">Tümü</string> <string name="all">Tümü</string>
<string name="uninstalled">Kaldırılmış</string> <string name="uninstalled">Kaldırılmış</string>
@@ -357,9 +341,6 @@
<string name="account_name">Hesap Adı</string> <string name="account_name">Hesap Adı</string>
<string name="keep_account">Hesabı Koru</string> <string name="keep_account">Hesabı Koru</string>
<string name="org_owned_work_profile">Kuruluşa Ait İş Profili</string> <string name="org_owned_work_profile">Kuruluşa Ait İş Profili</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">Şifrelemeyi Atla</string> <string name="skip_encryption">Şifrelemeyi Atla</string>
<string name="create">Oluştur</string> <string name="create">Oluştur</string>
<string name="suspend_personal_app">Kişisel Uygulamayı Askıya Al</string> <string name="suspend_personal_app">Kişisel Uygulamayı Askıya Al</string>
@@ -440,7 +421,6 @@
<string name="bt_share">Bluetooth Paylaşımı</string> <string name="bt_share">Bluetooth Paylaşımı</string>
<string name="share_location">Konumu Paylaş</string> <string name="share_location">Konumu Paylaş</string>
<string name="config_location">Konumu Yapılandır</string> <string name="config_location">Konumu Yapılandır</string>
<string name="nfc" translatable="false">NFC</string>
<string name="outgoing_beam">Giden Işın</string> <string name="outgoing_beam">Giden Işın</string>
<string name="usb_file_transfer">USB Dosya Aktarımı</string> <string name="usb_file_transfer">USB Dosya Aktarımı</string>
<string name="mount_physical_media">Fiziksel Medyayı Bağla</string> <string name="mount_physical_media">Fiziksel Medyayı Bağla</string>
@@ -501,7 +481,6 @@
<string name="logout_enabled">Çıkış Yapma Etkin</string> <string name="logout_enabled">Çıkış Yapma Etkin</string>
<string name="ephemeral_user">Geçici Kullanıcı</string> <string name="ephemeral_user">Geçici Kullanıcı</string>
<string name="affiliated_user">Bağlı Kullanıcı</string> <string name="affiliated_user">Bağlı Kullanıcı</string>
<string name="user_id" translatable="false">User ID</string>
<string name="user_serial_number">Kullanıcı Seri Numarası</string> <string name="user_serial_number">Kullanıcı Seri Numarası</string>
<string name="secondary_users">İkincil Kullanıcılar</string> <string name="secondary_users">İkincil Kullanıcılar</string>
<string name="no_secondary_users">İkincil Kullanıcı Yok</string> <string name="no_secondary_users">İkincil Kullanıcı Yok</string>

View File

@@ -70,8 +70,6 @@
<!--Permissions--> <!--Permissions-->
<string name="click_to_activate">点击以激活</string> <string name="click_to_activate">点击以激活</string>
<string name="device_admin">Device admin</string>
<string name="activate_jump" tools:ignore="TypographyEllipsis">激活...</string>
<string name="profile_owner">Profile owner</string> <string name="profile_owner">Profile owner</string>
<string name="device_owner">Device owner</string> <string name="device_owner">Device owner</string>
<string name="delegated_admins">委托管理员</string> <string name="delegated_admins">委托管理员</string>
@@ -112,9 +110,7 @@
<string name="activate_device_admin_here">在这里激活Device admin</string> <string name="activate_device_admin_here">在这里激活Device admin</string>
<!--Receiver--> <!--Receiver-->
<string name="onEnabled">OwnDroid已启用</string> <string name="create_work_profile_success">创建工作资料成功</string>
<string name="onDisabled">OwnDroid已禁用</string>
<string name="create_work_profile_success">OwnDroid创建工作资料成功</string>
<!--Dhizuku--> <!--Dhizuku-->
<string name="failed_to_init_dhizuku">Dhizuku初始化失败</string> <string name="failed_to_init_dhizuku">Dhizuku初始化失败</string>

View File

@@ -75,8 +75,6 @@
<!--Permissions--> <!--Permissions-->
<string name="click_to_activate">Click to activate</string> <string name="click_to_activate">Click to activate</string>
<string name="device_admin">Device admin</string>
<string name="activate_jump" tools:ignore="TypographyEllipsis">Activate...</string>
<string name="profile_owner">Profile owner</string> <string name="profile_owner">Profile owner</string>
<string name="device_owner">Device owner</string> <string name="device_owner">Device owner</string>
<string name="delegated_admins">Delegated admins</string> <string name="delegated_admins">Delegated admins</string>
@@ -91,7 +89,6 @@
<string name="add_delegated_admin">Add delegated admin</string> <string name="add_delegated_admin">Add delegated admin</string>
<string name="dhizuku_will_be_deactivated">Dhizuku will be deactivated</string> <string name="dhizuku_will_be_deactivated">Dhizuku will be deactivated</string>
<string name="reset_device_policy">Reset device policy</string> <string name="reset_device_policy">Reset device policy</string>
<string name="activate_device_admin_command" translatable="false">dpm set-active-admin com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver</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_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="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="device_info">Device info</string>
@@ -121,9 +118,7 @@
<string name="activate_device_admin_here">Activate Device admin here.</string> <string name="activate_device_admin_here">Activate Device admin here.</string>
<!--Receiver--> <!--Receiver-->
<string name="onEnabled">OwnDroid: Enabled</string> <string name="create_work_profile_success">Create work profile success</string>
<string name="onDisabled">OwnDroid: Disabled</string>
<string name="create_work_profile_success">OwnDroid: Create work profile success</string>
<!--Dhizuku--> <!--Dhizuku-->
<string name="dhizuku" translatable="false">Dhizuku</string> <string name="dhizuku" translatable="false">Dhizuku</string>