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

@@ -76,6 +76,7 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.AppInfo
import com.bintianqi.owndroid.AppInstallerActivity
import com.bintianqi.owndroid.AppInstallerViewModel
@@ -84,6 +85,7 @@ import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.getInstalledAppsFlags
import com.bintianqi.owndroid.installedApps
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.ErrorDialog
import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem
@@ -179,14 +181,11 @@ fun ApplicationsFeaturesScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Un
.verticalScroll(rememberScrollState())
.padding(bottom = 80.dp)
) {
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
val privilege by myPrivilege.collectAsStateWithLifecycle()
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.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) }
}
if(VERSION.SDK_INT >= 23) {
@@ -202,19 +201,19 @@ fun ApplicationsFeaturesScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Un
context.startActivity(Intent(context, AppInstallerActivity::class.java))
}
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) }
}
if(VERSION.SDK_INT >= 28) FunctionItem(R.string.install_existing_app, icon = R.drawable.install_mobile_fill0) {
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) }
}
if(profileOwner) {
if(privilege.work) {
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.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.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) }
}
}
@@ -445,9 +444,9 @@ fun DisableUserControlScreen(onNavigateUp: () -> Unit) {
}
LaunchedEffect(Unit) { refresh() }
MyLazyScaffold(R.string.disable_user_control, onNavigateUp) {
items(packages, { it.name }) {
ApplicationItem(it) {
dpm.setUserControlDisabledPackages(receiver, packages.minus(it).map { it.name })
items(packages, { it.name }) { info ->
ApplicationItem(info) {
dpm.setUserControlDisabledPackages(receiver, packages.minus(info).map { it.name })
refresh()
}
}
@@ -477,6 +476,7 @@ fun PermissionsManagerScreen(onNavigateUp: () -> Unit, param: PermissionsManager
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val privilege by myPrivilege.collectAsStateWithLifecycle()
var packageName by remember { mutableStateOf(packageNameParam ?: "") }
var selectedPermission by remember { mutableStateOf<PermissionItem?>(null) }
val statusMap = remember { mutableStateMapOf<String, Int>() }
@@ -551,7 +551,7 @@ fun PermissionsManagerScreen(onNavigateUp: () -> Unit, param: PermissionsManager
Column {
Text(selectedPermission!!.permission)
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.denied, PERMISSION_GRANT_STATE_DENIED)
@@ -581,9 +581,9 @@ fun DisableMeteredDataScreen(onNavigateUp: () -> Unit) {
}
LaunchedEffect(Unit) { refresh() }
MyLazyScaffold(R.string.disable_metered_data, onNavigateUp) {
items(packages, { it.name }) {
ApplicationItem(it) {
dpm.setMeteredDataDisabledPackages(receiver, packages.minus(it).map { it.name })
items(packages, { it.name }) { info ->
ApplicationItem(info) {
dpm.setMeteredDataDisabledPackages(receiver, packages.minus(info).map { it.name })
refresh()
}
}
@@ -732,9 +732,9 @@ fun KeepUninstalledPackagesScreen(onNavigateUp: () -> Unit) {
}
LaunchedEffect(Unit) { refresh() }
MyLazyScaffold(R.string.keep_uninstalled_packages, onNavigateUp) {
items(packages, { it.name }) {
ApplicationItem(it) {
dpm.setKeepUninstalledPackages(receiver, packages.minus(it).map { it.name })
items(packages, { it.name }) { info ->
ApplicationItem(info) {
dpm.setKeepUninstalledPackages(receiver, packages.minus(info).map { it.name })
refresh()
}
}
@@ -798,9 +798,9 @@ fun CrossProfilePackagesScreen(onNavigateUp: () -> Unit) {
}
LaunchedEffect(Unit) { refresh() }
MyLazyScaffold(R.string.cross_profile_apps, onNavigateUp) {
items(packages, { it.name }) {
ApplicationItem(it) {
dpm.setCrossProfilePackages(receiver, packages.minus(it).map { it.name }.toSet())
items(packages, { it.name }) { info ->
ApplicationItem(info) {
dpm.setCrossProfilePackages(receiver, packages.minus(info).map { it.name }.toSet())
refresh()
}
}
@@ -1062,6 +1062,7 @@ fun EnableSystemAppScreen(onNavigateUp: () -> Unit) {
) {
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.pm.IPackageInstaller
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.UserManager
import android.util.Log
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.DrawableRes
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
import androidx.core.graphics.drawable.toBitmap
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.Receiver
import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.ShortcutsReceiverActivity
import com.bintianqi.owndroid.backToHomeStateFlow
import com.bintianqi.owndroid.myPrivilege
import com.rosan.dhizuku.api.Dhizuku
import com.rosan.dhizuku.api.Dhizuku.binderWrapper
import com.rosan.dhizuku.api.DhizukuBinderWrapper
@@ -39,8 +43,6 @@ import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonArray
import java.io.OutputStream
lateinit var addDeviceAdmin: ActivityResultLauncher<Intent>
val Context.isDeviceOwner: Boolean
get() {
val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
@@ -59,11 +61,6 @@ val Context.isProfileOwner: Boolean
return dpm.isProfileOwnerApp("com.bintianqi.owndroid")
}
val Context.isDeviceAdmin: Boolean
get() {
return getDPM().isAdminActive(getReceiver())
}
val Context.dpcPackageName: String
get() {
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")
private fun binderWrapperDevicePolicyManager(appContext: Context): DevicePolicyManager? {
try {
@@ -544,10 +537,10 @@ fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? {
fun setDefaultAffiliationID(context: Context) {
if(VERSION.SDK_INT < 26) return
val sp = SharedPrefs(context)
val privilege = myPrivilege.value
if(!sp.isDefaultAffiliationIdSet) {
try {
val um = context.getSystemService(Context.USER_SERVICE) as UserManager
if(context.isDeviceOwner || (!um.isSystemUser && context.isProfileOwner)) {
if(privilege.device || (!privilege.primary && privilege.profile)) {
val dpm = context.getDPM()
val receiver = context.getReceiver()
val affiliationIDs = dpm.getAffiliationIds(receiver)
@@ -596,3 +589,31 @@ fun parsePackageInstallerMessage(context: Context, result: Intent): String {
else -> ""
} + 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.ProxyInfo
import android.net.StaticIpConfiguration
import android.net.Uri
import android.net.wifi.WifiConfiguration
import android.net.wifi.WifiManager
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.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.core.os.bundleOf
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.ChoosePackageContract
import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.formatDate
import com.bintianqi.owndroid.formatFileSize
import com.bintianqi.owndroid.humanReadableDate
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.ErrorDialog
@@ -163,29 +164,24 @@ import kotlin.reflect.jvm.jvmErasure
@Composable
fun NetworkScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
val dhizuku = SharedPrefs(context).dhizuku
val privilege by myPrivilege.collectAsStateWithLifecycle()
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) {
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) }
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) }
}
if(VERSION.SDK_INT >= 24) {
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) }
}
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) }
}
if(VERSION.SDK_INT >= 31) {
@@ -194,7 +190,7 @@ fun NetworkScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
if(VERSION.SDK_INT >= 33) {
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) }
}
}
@@ -207,10 +203,10 @@ fun NetworkOptionsScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val privilege by myPrivilege.collectAsStateWithLifecycle()
var dialog by remember { mutableIntStateOf(0) }
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,
getState = { dpm.hasLockdownAdminConfiguredNetworks(receiver) }, onCheckedChange = { dpm.setConfiguredNetworksLockdownState(receiver,it) },
onClickBlank = { dialog = 1 }
@@ -265,9 +261,8 @@ fun WifiScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateTo
}
HorizontalPager(state = pagerState, verticalAlignment = Alignment.Top) { page ->
if(page == 0) {
val wm = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
val deviceOwner = context.isDeviceOwner
val orgProfileOwner = context.getDPM().isOrgProfile(context.getReceiver())
val wm = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val privilege by myPrivilege.collectAsStateWithLifecycle()
@Suppress("DEPRECATION") Column(
modifier = Modifier.fillMaxSize().padding(top = 12.dp)
) {
@@ -299,10 +294,10 @@ fun WifiScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateTo
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 }
}
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.wifi_ssid_policy) { onNavigate(WifiSsidPolicyScreen) }
}
@@ -315,7 +310,6 @@ fun WifiScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateTo
}
}
if(wifiMacDialog && VERSION.SDK_INT >= 24) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
AlertDialog(
@@ -344,7 +338,7 @@ fun WifiScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateTo
@Composable
private fun SavedNetworks(onNavigateToUpdateNetwork: (Bundle) -> Unit) {
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>() }
var networkDetailsDialog by remember { mutableIntStateOf(-1) } // -1:Hidden, 0+:Index of configuredNetworks
val coroutine = rememberCoroutineScope()
@@ -688,7 +682,7 @@ private fun AddNetworkScreen(wifiConfig: WifiConfiguration? = null, onNavigateUp
}
Button(
onClick = {
val wm = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
val wm = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
try {
val config = WifiConfiguration()
config.status = status
@@ -899,7 +893,7 @@ fun NetworkStats.toBucketList(): List<NetworkStats.Bucket> {
@Composable
fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkStatsViewer) -> Unit) {
val context = LocalContext.current
val deviceOwner = context.isDeviceOwner
val privilege by myPrivilege.collectAsStateWithLifecycle()
val fm = LocalFocusManager.current
val nsm = context.getSystemService(NetworkStatsManager::class.java)
val coroutine = rememberCoroutineScope()
@@ -971,7 +965,7 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
NetworkStatsTarget.entries.forEach {
if(
VERSION.SDK_INT >= it.minApi &&
(deviceOwner || it != NetworkStatsTarget.Device) &&
(privilege.device || it != NetworkStatsTarget.Device) &&
((queryType == 1 && (it == NetworkStatsTarget.Device || it == NetworkStatsTarget.User)) ||
(queryType == 2 && (it == NetworkStatsTarget.Uid || it == NetworkStatsTarget.UidTag || it == NetworkStatsTarget.UidTagState)))
) DropdownMenuItem(
@@ -1120,7 +1114,7 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
if(VERSION.SDK_INT >= 24 && (target == NetworkStatsTarget.UidTag || target == NetworkStatsTarget.UidTagState))
ExposedDropdownMenuBox(
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 readOnly by rememberSaveable { mutableStateOf(true) }
@@ -1596,7 +1590,7 @@ fun RecommendedGlobalProxyScreen(onNavigateUp: () -> Unit) {
Toast.makeText(context, R.string.invalid_config, Toast.LENGTH_SHORT).show()
return@Button
}
val uri = Uri.parse(proxyUri)
val uri = proxyUri.toUri()
val port: Int
try {
port = proxyPort.toInt()
@@ -1827,8 +1821,8 @@ fun AddPreferentialNetworkServiceConfigScreen(route: AddPreferentialNetworkServi
title = R.string.block_non_matching_networks,
state = blockNonMatching, onCheckedChange = { blockNonMatching = it }, padding = false
)
val includedUidsLegal = includedUids.lines().filter { it.isNotBlank() }.let {
it.isEmpty() || (it.all { it.toIntOrNull() != null } && excludedUids.isBlank())
val includedUidsLegal = includedUids.lines().filter { it.isNotBlank() }.let { uid ->
uid.isEmpty() || (uid.all { it.toIntOrNull() != null } && excludedUids.isBlank())
}
OutlinedTextField(
value = includedUids, onValueChange = { includedUids = it }, minLines = 2,
@@ -1837,8 +1831,8 @@ fun AddPreferentialNetworkServiceConfigScreen(route: AddPreferentialNetworkServi
isError = !includedUidsLegal,
modifier = Modifier.fillMaxWidth().padding(bottom = 6.dp)
)
val excludedUidsLegal = excludedUids.lines().filter { it.isNotBlank() }.let {
it.isEmpty() || (it.all { it.toIntOrNull() != null } && includedUids.isBlank())
val excludedUidsLegal = excludedUids.lines().filter { it.isNotBlank() }.let { uid ->
uid.isEmpty() || (uid.all { it.toIntOrNull() != null } && includedUids.isBlank())
}
OutlinedTextField(
value = excludedUids, onValueChange = { excludedUids = it }, minLines = 2,
@@ -1852,7 +1846,7 @@ fun AddPreferentialNetworkServiceConfigScreen(route: AddPreferentialNetworkServi
try {
val config = PreferentialNetworkServiceConfig.Builder().apply {
setEnabled(enabled)
if(enabled) setNetworkId(id.toInt())
if(enabled) setNetworkId(id)
setFallbackToDefaultConnectionAllowed(allowFallback)
setExcludedUids(excludedUids.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
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
@OptIn(ExperimentalLayoutApi::class)
@RequiresApi(28)
@Composable
fun AddApnSettingScreen(origin: ApnSetting?, onNavigateUp: () -> Unit) {
@@ -2099,14 +2093,14 @@ fun AddApnSettingScreen(origin: ApnSetting?, onNavigateUp: () -> Unit) {
OutlinedTextField(
mtuV4, { mtuV4 = it }, Modifier.fillMaxWidth(0.49F),
label = { Text("MTU (IPv4)") },
isError = !mtuV4.isEmpty() && mtuV4.toIntOrNull() == null,
isError = mtuV4.isNotEmpty() && mtuV4.toIntOrNull() == null,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Next),
keyboardActions = KeyboardActions { fr.requestFocus() }
)
OutlinedTextField(
mtuV6, { mtuV6 = it }, Modifier.focusRequester(fr).fillMaxWidth(0.96F),
label = { Text("MTU (IPv6)") },
isError = !mtuV6.isEmpty() && mtuV6.toIntOrNull() == null,
isError = mtuV6.isNotEmpty() && mtuV6.toIntOrNull() == null,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions { fm.clearFocus() }
)
@@ -2194,7 +2188,7 @@ fun AddApnSettingScreen(origin: ApnSetting?, onNavigateUp: () -> Unit) {
setMmsProxyAddress(mmsProxyAddress)
mmsProxyPort.toIntOrNull()?.let { setMmsProxyPort(it) }
}
setMmsc(Uri.parse(mmsc))
setMmsc(mmsc.toUri())
if(VERSION.SDK_INT >= 33) {
mtuV4.toIntOrNull()?.let { setMtuV4(it) }
mtuV6.toIntOrNull()?.let { setMtuV6(it) }
@@ -2238,7 +2232,7 @@ fun AddApnSettingScreen(origin: ApnSetting?, onNavigateUp: () -> Unit) {
AlertDialog(
title = { Text(if(dialog == 1) "Proxy" else "MMS proxy") },
text = {
val fm = LocalFocusManager.current
val focusManager = LocalFocusManager.current
Column {
OutlinedTextField(
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,
label = { Text(stringResource(R.string.port)) },
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.unit.dp
import androidx.core.content.ContextCompat.startActivity
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem
@@ -86,38 +88,30 @@ import kotlinx.serialization.Serializable
@Composable
fun PasswordScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
val context = LocalContext.current
val deviceAdmin = context.isDeviceAdmin
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
val privilege by myPrivilege.collectAsStateWithLifecycle()
var dialog by remember { mutableIntStateOf(0) }
MyScaffold(R.string.password_and_keyguard, onNavigateUp, 0.dp) {
FunctionItem(R.string.password_info, icon = R.drawable.info_fill0) { onNavigate(PasswordInfo) }
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) }
}
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 && (deviceOwner || profileOwner)) {
if(VERSION.SDK_INT >= 31) {
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) }
}
if(deviceOwner) {
FunctionItem(R.string.disable_keyguard_features, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(KeyguardDisabledFeatures) }
if(privilege.device) {
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.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 }
}
if(deviceAdmin){
FunctionItem(R.string.pwd_history, icon = R.drawable.history_fill0) { dialog = 5 }
}
if(VERSION.SDK_INT < 31 && (deviceOwner || profileOwner)) {
FunctionItem(R.string.pwd_history, icon = R.drawable.history_fill0) { dialog = 5 }
if(VERSION.SDK_INT < 31) {
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 dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
val privilege by myPrivilege.collectAsStateWithLifecycle()
var dialog by remember { mutableIntStateOf(0) } // 0:none, 1:password complexity
MyScaffold(R.string.password_info, onNavigateUp, 0.dp) {
if(VERSION.SDK_INT >= 29) {
@@ -231,10 +224,8 @@ fun PasswordInfoScreen(onNavigateUp: () -> Unit) {
}
InfoItem(R.string.current_password_complexity, text, true) { dialog = 1 }
}
if(deviceOwner || profileOwner) {
InfoItem(R.string.password_sufficient, dpm.isActivePasswordSufficient.yesOrNo)
}
if(VERSION.SDK_INT >= 28 && profileOwner && dpm.isManagedProfile(receiver)) {
InfoItem(R.string.password_sufficient, dpm.isActivePasswordSufficient.yesOrNo)
if(VERSION.SDK_INT >= 28 && privilege.work) {
InfoItem(R.string.unified_password, dpm.isUsingUnifiedPassword(receiver).yesOrNo)
}
}
@@ -367,7 +358,7 @@ fun ResetPasswordScreen(onNavigateUp: () -> Unit) {
focusMgr.clearFocus()
},
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()
) {
Text(stringResource(R.string.reset_password_with_token))

View File

@@ -1,18 +1,14 @@
package com.bintianqi.owndroid.dpm
import android.annotation.SuppressLint
import android.app.admin.DevicePolicyManager
import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Binder
import android.os.Build.VERSION
import android.os.Bundle
import android.os.IBinder
import android.os.RemoteException
import android.os.UserManager
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.annotation.Keep
@@ -21,7 +17,6 @@ import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
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.window.Dialog
import androidx.core.os.bundleOf
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.ChoosePackageContract
import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.backToHomeStateFlow
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.*
import com.bintianqi.owndroid.updatePrivilege
import com.bintianqi.owndroid.writeClipBoard
import com.bintianqi.owndroid.yesOrNo
import com.rosan.dhizuku.api.Dhizuku
@@ -61,19 +59,15 @@ import rikka.sui.Sui
@Serializable object Permissions
@SuppressLint("NewApi")
@Composable
fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateToShizuku: (Bundle) -> Unit) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceAdmin = context.isDeviceAdmin
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
val privilege by myPrivilege.collectAsStateWithLifecycle()
var dialog by remember { mutableIntStateOf(0) }
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) {
if(!dpm.isDeviceOwnerApp(context.packageName)) {
SwitchItem(
@@ -83,19 +77,15 @@ fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNav
onClickBlank = { dialog = 4 }
)
}
FunctionItem(
R.string.device_admin, stringResource(if(deviceAdmin) R.string.activated else R.string.deactivated),
operation = { onNavigate(DeviceAdmin) }
)
if(profileOwner || !userManager.isSystemUser) {
if(privilege.profile || !privilege.primary) {
FunctionItem(
R.string.profile_owner, stringResource(if(profileOwner) R.string.activated else R.string.deactivated),
R.string.profile_owner, stringResource(if(privilege.profile) R.string.activated else R.string.deactivated),
operation = { onNavigate(ProfileOwner) }
)
}
if(!profileOwner && userManager.isSystemUser) {
if(!privilege.profile && privilege.primary) {
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) }
)
}
@@ -130,25 +120,24 @@ fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNav
Toast.makeText(context, R.string.shizuku_not_started, Toast.LENGTH_SHORT).show()
}
}
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner))
FunctionItem(R.string.delegated_admins) { onNavigate(DelegatedAdmins) }
if(VERSION.SDK_INT >= 26) FunctionItem(R.string.delegated_admins) { onNavigate(DelegatedAdmins) }
FunctionItem(R.string.device_info, icon = R.drawable.perm_device_information_fill0) { onNavigate(DeviceInfo) }
if(VERSION.SDK_INT >= 24 && (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 }
}
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 }
}
if(enrollmentSpecificId != "") {
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) }
}
if(VERSION.SDK_INT >= 24 && deviceAdmin) {
if(VERSION.SDK_INT >= 24) {
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) }
}
}
@@ -174,7 +163,7 @@ fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNav
text = {
val focusMgr = LocalFocusManager.current
LaunchedEffect(Unit) {
if(dialog == 1) input = dpm.enrollmentSpecificId
if(dialog == 1 && VERSION.SDK_INT >= 31) input = dpm.enrollmentSpecificId
}
Column {
if(dialog != 4) OutlinedTextField(
@@ -220,8 +209,8 @@ fun PermissionsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNav
TextButton(
onClick = {
try {
if(dialog == 2) dpm.setOrganizationName(receiver, input)
if(dialog == 3) dpm.setOrganizationId(input)
if(dialog == 2 && VERSION.SDK_INT >= 24) dpm.setOrganizationName(receiver, input)
if(dialog == 3 && VERSION.SDK_INT >= 31) dpm.setOrganizationId(input)
dialog = 0
} catch(_: IllegalStateException) {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
@@ -250,6 +239,7 @@ private fun toggleDhizukuMode(status: Boolean, context: Context) {
if(dhizukuPermissionGranted()) {
sp.dhizuku = true
Dhizuku.init(context)
updatePrivilege(context)
backToHomeStateFlow.value = true
} else {
Dhizuku.requestPermission(object: DhizukuRequestPermissionListener() {
@@ -258,6 +248,7 @@ private fun toggleDhizukuMode(status: Boolean, context: Context) {
if(grantResult == PackageManager.PERMISSION_GRANTED) {
sp.dhizuku = true
Dhizuku.init(context)
updatePrivilege(context)
backToHomeStateFlow.value = true
} else {
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
@Composable
@@ -378,7 +310,8 @@ fun ProfileOwnerScreen(onNavigateUp: () -> Unit) {
val dpm = context.getDPM()
val receiver = context.getReceiver()
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) {
Text(stringResource(if(profileOwner) R.string.activated else R.string.deactivated), style = typography.titleLarge)
Spacer(Modifier.padding(vertical = 5.dp))
@@ -432,7 +365,8 @@ fun DeviceOwnerScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current
val dpm = context.getDPM()
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) {
Text(text = stringResource(if(deviceOwner) R.string.activated else R.string.deactivated), style = typography.titleLarge)
Spacer(Modifier.padding(vertical = 5.dp))
@@ -644,15 +578,15 @@ fun AddDelegatedAdminScreen(data: AddDelegatedAdmin, onNavigateUp: () -> Unit) {
fun DeviceInfoScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val privilege by myPrivilege.collectAsStateWithLifecycle()
var dialog by remember { mutableIntStateOf(0) }
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)
}
if(VERSION.SDK_INT >= 33) {
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(
DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE to R.string.es_inactive,
@@ -670,7 +604,7 @@ fun DeviceInfoScreen(onNavigateUp: () -> Unit) {
}
val adminList = dpm.activeAdmins
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(
@@ -766,6 +700,7 @@ fun SupportMessageScreen(onNavigateUp: () -> Unit) {
@Composable
fun TransferOwnershipScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current
val privilege by myPrivilege.collectAsStateWithLifecycle()
val focusMgr = LocalFocusManager.current
var input by remember { mutableStateOf("") }
val componentName = ComponentName.unflattenFromString(input)
@@ -793,7 +728,7 @@ fun TransferOwnershipScreen(onNavigateUp: () -> Unit) {
text = {
Text(stringResource(
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
))
},
@@ -805,11 +740,12 @@ fun TransferOwnershipScreen(onNavigateUp: () -> Unit) {
try {
dpm.transferOwnership(receiver, componentName!!, null)
context.showOperationResultToast(true)
updatePrivilege(context)
dialog = false
backToHomeStateFlow.value = true
onNavigateUp()
} catch(e: Exception) {
e.printStackTrace()
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
context.showOperationResultToast(false)
}
},
colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error)
@@ -825,14 +761,3 @@ fun TransferOwnershipScreen(onNavigateUp: () -> Unit) {
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.text.selection.SelectionContainer
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
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.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.IUserService
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.ui.MySmallTitleScaffold
import com.bintianqi.owndroid.updatePrivilege
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
@@ -46,17 +48,13 @@ import rikka.shizuku.Shizuku
@Serializable object ShizukuScreen
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccountsViewer: (Accounts) -> Unit) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val coScope = rememberCoroutineScope()
val privilege by myPrivilege.collectAsStateWithLifecycle()
val coroutine = rememberCoroutineScope()
val outputTextScrollState = rememberScrollState()
var outputText by rememberSaveable { mutableStateOf("") }
var showDeviceOwnerButton by remember { mutableStateOf(!context.isDeviceOwner) }
var showOrgProfileOwnerButton by remember { mutableStateOf(true) }
val binder = navArgs.getBinder("binder")!!
var service by remember { mutableStateOf<IUserService?>(null) }
LaunchedEffect(Unit) {
@@ -70,7 +68,7 @@ fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccount
Button(
onClick = {
coScope.launch{
coroutine.launch{
outputText = service!!.execute("dpm list-owners")
outputTextScrollState.animateScrollTo(0)
}
@@ -81,7 +79,7 @@ fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccount
}
Button(
onClick = {
coScope.launch{
coroutine.launch{
outputText = service!!.execute("pm list users")
outputTextScrollState.animateScrollTo(0)
}
@@ -100,7 +98,7 @@ fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccount
onNavigateToAccountsViewer(Accounts(accounts))
} catch(_: Exception) {
outputText = service!!.execute("dumpsys account")
coScope.launch{
coroutine.launch{
outputTextScrollState.animateScrollTo(0)
}
}
@@ -111,39 +109,37 @@ fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccount
}
Spacer(Modifier.padding(vertical = 5.dp))
AnimatedVisibility(showDeviceOwnerButton, modifier = Modifier.align(Alignment.CenterHorizontally)) {
AnimatedVisibility(!privilege.device, modifier = Modifier.align(Alignment.CenterHorizontally)) {
Button(
onClick = {
coScope.launch{
coroutine.launch{
outputText = service!!.execute(context.getString(R.string.dpm_activate_do_command))
outputTextScrollState.animateScrollTo(0)
delay(500)
showDeviceOwnerButton = !context.isDeviceOwner
updatePrivilege(context)
}
}
) {
Text(text = stringResource(R.string.activate_device_owner))
}
}
if(VERSION.SDK_INT >= 30 && context.isProfileOwner && dpm.isManagedProfile(receiver) && !dpm.isOrganizationOwnedDeviceWithManagedProfile) {
AnimatedVisibility(showOrgProfileOwnerButton) {
Button(
onClick = {
coScope.launch{
val userID = Binder.getCallingUid() / 100000
outputText = service!!.execute(
"dpm mark-profile-owner-on-organization-owned-device --user $userID com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver"
)
outputTextScrollState.animateScrollTo(0)
delay(500)
showOrgProfileOwnerButton = !dpm.isOrganizationOwnedDeviceWithManagedProfile
}
},
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
Text(text = stringResource(R.string.activate_org_profile))
}
AnimatedVisibility(VERSION.SDK_INT >= 30 && privilege.work && !privilege.org) {
Button(
onClick = {
coroutine.launch{
val userID = Binder.getCallingUid() / 100000
outputText = service!!.execute(
"dpm mark-profile-owner-on-organization-owned-device --user $userID com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver"
)
outputTextScrollState.animateScrollTo(0)
delay(500)
updatePrivilege(context)
}
},
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
Text(text = stringResource(R.string.activate_org_profile))
}
}

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.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.ChoosePackageContract
import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.NotificationUtils
@@ -123,6 +124,7 @@ import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.formatFileSize
import com.bintianqi.owndroid.humanReadableDate
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.parseDate
import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CheckBoxItem
@@ -160,62 +162,54 @@ fun SystemManagerScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
val dpm = context.getDPM()
val receiver = context.getReceiver()
val sp = SharedPrefs(context)
val dhizuku = sp.dhizuku
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
val privilege by myPrivilege.collectAsStateWithLifecycle()
var dialog by remember { mutableIntStateOf(0) }
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) }
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) }
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 }
}
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 }
}
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_timezone, icon = R.drawable.schedule_fill0) { onNavigate(ChangeTimeZone) }
}
/*if(VERSION.SDK_INT >= 28 && (deviceOwner || profileOwner))
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) }
if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) {
if(VERSION.SDK_INT >= 23) {
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) }
}
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) }
}
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) }
}
if(deviceOwner || profileOwner) {
FunctionItem(R.string.ca_cert, icon = R.drawable.license_fill0) { onNavigate(CaCert) }
}
if(VERSION.SDK_INT >= 26 && !dhizuku && (deviceOwner || dpm.isOrgProfile(receiver))) {
FunctionItem(R.string.ca_cert, icon = R.drawable.license_fill0) { onNavigate(CaCert) }
if(VERSION.SDK_INT >= 26 && !privilege.dhizuku && (privilege.device || privilege.org)) {
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) }
}
if(VERSION.SDK_INT >= 23 && (deviceOwner || dpm.isOrgProfile(receiver))) {
FunctionItem(R.string.disable_account_management, icon = R.drawable.account_circle_fill0) { onNavigate(DisableAccountManagement) }
if(VERSION.SDK_INT >= 23 && (privilege.device || privilege.org)) {
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) }
}
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) }
}
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) }
}
}
@@ -253,27 +247,21 @@ fun SystemOptionsScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
val um = context.getSystemService(Context.USER_SERVICE) as UserManager
val privilege by myPrivilege.collectAsStateWithLifecycle()
var dialog by remember { mutableIntStateOf(0) }
MyScaffold(R.string.options, onNavigateUp, 0.dp) {
if(deviceOwner || profileOwner) {
SwitchItem(R.string.disable_cam, icon = R.drawable.photo_camera_fill0,
getState = { dpm.getCameraDisabled(null) }, onCheckedChange = { dpm.setCameraDisabled(receiver,it) }
)
}
if(deviceOwner || profileOwner) {
SwitchItem(R.string.disable_screen_capture, icon = R.drawable.screenshot_fill0,
getState = { dpm.getScreenCaptureDisabled(null) }, onCheckedChange = { dpm.setScreenCaptureDisabled(receiver,it) }
)
}
if(VERSION.SDK_INT >= 34 && (deviceOwner || (profileOwner && dpm.isAffiliatedUser))) {
SwitchItem(R.string.disable_cam, icon = R.drawable.photo_camera_fill0,
getState = { dpm.getCameraDisabled(null) }, onCheckedChange = { dpm.setCameraDisabled(receiver,it) }
)
SwitchItem(R.string.disable_screen_capture, icon = R.drawable.screenshot_fill0,
getState = { dpm.getScreenCaptureDisabled(null) }, onCheckedChange = { dpm.setScreenCaptureDisabled(receiver,it) }
)
if(VERSION.SDK_INT >= 34 && (privilege.device || (privilege.profile && privilege.affiliated))) {
SwitchItem(R.string.disable_status_bar, icon = R.drawable.notifications_fill0,
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) {
SwitchItem(R.string.auto_time, icon = R.drawable.schedule_fill0,
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)
}
}
if(deviceOwner || profileOwner) {
SwitchItem(R.string.master_mute, icon = R.drawable.volume_off_fill0,
getState = { dpm.isMasterVolumeMuted(receiver) }, onCheckedChange = { dpm.setMasterVolumeMuted(receiver,it) }
)
}
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) {
SwitchItem(R.string.master_mute, icon = R.drawable.volume_off_fill0,
getState = { dpm.isMasterVolumeMuted(receiver) }, onCheckedChange = { dpm.setMasterVolumeMuted(receiver,it) }
)
if(VERSION.SDK_INT >= 26) {
SwitchItem(R.string.backup_service, icon = R.drawable.backup_fill0,
getState = { dpm.isBackupServiceEnabled(receiver) }, onCheckedChange = { dpm.setBackupServiceEnabled(receiver,it) },
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,
getState = { dpm.getBluetoothContactSharingDisabled(receiver) },
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,
getState = { dpm.isCommonCriteriaModeEnabled(receiver) }, onCheckedChange = { dpm.setCommonCriteriaModeEnabled(receiver,it) },
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(
R.string.disable_usb_signal, icon = R.drawable.usb_fill0, getState = { !dpm.isUsbDataSignalingEnabled },
onCheckedChange = { dpm.isUsbDataSignalingEnabled = !it },
@@ -340,24 +326,21 @@ fun KeyguardScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
val privilege by myPrivilege.collectAsStateWithLifecycle()
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(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
Button(
onClick = { context.showOperationResultToast(dpm.setKeyguardDisabled(receiver, true)) },
enabled = deviceOwner || (VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser),
modifier = Modifier.fillMaxWidth(0.49F)
) {
Text(stringResource(R.string.disable))
}
Button(
onClick = { context.showOperationResultToast(dpm.setKeyguardDisabled(receiver, false)) },
enabled = deviceOwner || (VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser),
modifier = Modifier.fillMaxWidth(0.96F)
) {
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)
Spacer(Modifier.padding(vertical = 2.dp))
var flag by remember { mutableIntStateOf(0) }
if(VERSION.SDK_INT >= 26 && profileOwner && dpm.isManagedProfile(receiver)) {
if(VERSION.SDK_INT >= 26 && privilege.work) {
CheckBoxItem(
R.string.evict_credential_encryption_key,
flag and FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY != 0
@@ -380,12 +363,11 @@ fun KeyguardScreen(onNavigateUp: () -> Unit) {
onClick = {
if(VERSION.SDK_INT >= 26) dpm.lockNow(flag) else dpm.lockNow()
},
enabled = context.isDeviceAdmin,
modifier = Modifier.fillMaxWidth()
) {
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)
}
}
@@ -393,7 +375,6 @@ fun KeyguardScreen(onNavigateUp: () -> Unit) {
@Serializable object HardwareMonitor
@OptIn(ExperimentalMaterial3Api::class)
@RequiresApi(24)
@Composable
fun HardwareMonitorScreen(onNavigateUp: () -> Unit) {
@@ -1332,7 +1313,7 @@ fun CaCertScreen(onNavigateUp: () -> Unit) {
text = {
if(dialog == 3) Text(stringResource(R.string.uninstall_all_user_ca_cert))
else {
var text = ""
var text: String
val sha256 = MessageDigest.getInstance("SHA-256").digest(caCertByteArray).toHexString()
try {
val cf = CertificateFactory.getInstance("X.509")
@@ -1634,6 +1615,7 @@ fun WipeDataScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
val dpm = context.getDPM()
val privilege by myPrivilege.collectAsStateWithLifecycle()
val focusMgr = LocalFocusManager.current
var flag by remember { mutableIntStateOf(0) }
var warning by remember { mutableStateOf(false) }
@@ -1642,7 +1624,7 @@ fun WipeDataScreen(onNavigateUp: () -> Unit) {
var reason by remember { mutableStateOf("") }
MyScaffold(R.string.wipe_data, onNavigateUp) {
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 }
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 }
@@ -1667,7 +1649,7 @@ fun WipeDataScreen(onNavigateUp: () -> Unit) {
Text("WipeData")
}
}
if (VERSION.SDK_INT >= 34 && context.isDeviceOwner) {
if (VERSION.SDK_INT >= 34 && privilege.device) {
Button(
onClick = {
focusMgr.clearFocus()

View File

@@ -10,11 +10,13 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.SwitchItem
@@ -33,14 +35,12 @@ data class Restriction(
@RequiresApi(24)
@Composable
fun UserRestrictionScreen(onNavigateUp: () -> Unit, onNavigate: (Int, List<Restriction>) -> Unit) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val privilege by myPrivilege.collectAsStateWithLifecycle()
MyScaffold(R.string.user_restriction, onNavigateUp, 0.dp) {
Spacer(Modifier.padding(vertical = 2.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(context.isProfileOwner && dpm.isManagedProfile(receiver)) {
if(privilege.profile) { Text(text = stringResource(R.string.profile_owner_is_restricted), modifier = Modifier.padding(start = 16.dp)) }
if(privilege.work) {
Text(text = stringResource(R.string.some_features_invalid_in_work_profile), modifier = Modifier.padding(start = 16.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.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.parseTimestamp
import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem
@@ -89,28 +91,25 @@ fun UsersScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
val privilege by myPrivilege.collectAsStateWithLifecycle()
var dialog by remember { mutableIntStateOf(0) }
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.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.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) }
}
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) }
}
if(deviceOwner || profileOwner) {
FunctionItem(R.string.change_username, icon = R.drawable.edit_fill0) { onNavigate(ChangeUsername) }
}
if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) {
FunctionItem(R.string.change_username, icon = R.drawable.edit_fill0) { onNavigate(ChangeUsername) }
if(VERSION.SDK_INT >= 23) {
var changeUserIconDialog by remember { mutableStateOf(false) }
var bitmap: Bitmap? by remember { mutableStateOf(null) }
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()
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) }
}
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) }
}
}
@@ -190,6 +189,7 @@ fun UserInfoScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val privilege by myPrivilege.collectAsStateWithLifecycle()
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
val user = Process.myUserHandle()
var infoDialog by remember { mutableIntStateOf(0) }
@@ -205,10 +205,8 @@ fun UserInfoScreen(onNavigateUp: () -> Unit) {
}
if (VERSION.SDK_INT >= 28) {
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.affiliated_user, dpm.isAffiliatedUser.yesOrNo)
InfoItem(R.string.ephemeral_user, dpm.isEphemeralUser(receiver).yesOrNo)
InfoItem(R.string.affiliated_user, privilege.affiliated.yesOrNo)
}
InfoItem(R.string.user_id, (Binder.getCallingUid() / 100000).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))
Button(
onClick = {
list.removeAll(listOf(""))
list.removeAll(setOf(""))
dpm.setAffiliationIds(receiver, list.toSet())
context.showOperationResultToast(true)
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.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.myPrivilege
import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.CopyTextButton
@@ -69,26 +71,16 @@ import kotlinx.serialization.Serializable
@Composable
fun WorkProfileScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val profileOwner = context.isProfileOwner
val privilege by myPrivilege.collectAsStateWithLifecycle()
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) }
}
if(VERSION.SDK_INT < 24 || dpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)) {
FunctionItem(R.string.create_work_profile, icon = R.drawable.work_fill0) { onNavigate(CreateWorkProfile) }
}
if(dpm.isOrgProfile(receiver)) {
if(privilege.org) {
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) }
}
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.intent_filter, icon = R.drawable.filter_alt_fill0) { onNavigate(CrossProfileIntentFilter) }
FunctionItem(R.string.delete_work_profile, icon = R.drawable.delete_forever_fill0) { onNavigate(DeleteWorkProfile) }
}
}