Configure Preferential network service

Transfer ownership: get ComponentName by unflatten string
Bump app version to v6.1(33)
Update AGP to 8.7.2
This commit is contained in:
BinTianqi
2024-11-02 18:47:52 +08:00
parent 5e8ea061ca
commit e6fa6f1305
8 changed files with 237 additions and 43 deletions

View File

@@ -24,8 +24,8 @@ android {
applicationId = "com.bintianqi.owndroid"
minSdk = 21
targetSdk = 34
versionCode = 32
versionName = "6.0"
versionCode = 33
versionName = "6.1"
multiDexEnabled = false
}

View File

@@ -12,6 +12,7 @@ import android.app.admin.DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_192
import android.app.admin.DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_EAP
import android.app.admin.DevicePolicyManager.WIFI_SECURITY_OPEN
import android.app.admin.DevicePolicyManager.WIFI_SECURITY_PERSONAL
import android.app.admin.PreferentialNetworkServiceConfig
import android.app.admin.WifiSsidPolicy
import android.app.admin.WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST
import android.app.admin.WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST
@@ -59,6 +60,10 @@ import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
@@ -111,6 +116,7 @@ import com.bintianqi.owndroid.ui.SubPageItem
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.TopBar
import com.bintianqi.owndroid.writeClipBoard
import kotlin.math.max
@Composable
fun Network(navCtrl: NavHostController) {
@@ -147,6 +153,7 @@ fun Network(navCtrl: NavHostController) {
composable(route = "RecommendedGlobalProxy") { RecommendedGlobalProxy() }
composable(route = "NetworkLog") { NetworkLog() }
composable(route = "WifiAuthKeypair") { WifiAuthKeypair() }
composable(route = "PreferentialNetworkService") { PreferentialNetworkService() }
composable(route = "APN") { APN() }
}
}
@@ -217,6 +224,9 @@ private fun Home(navCtrl:NavHostController, scrollState: ScrollState, wifiMacDia
if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.wifi_auth_keypair, "", R.drawable.key_fill0) { navCtrl.navigate("WifiAuthKeypair") }
}
if(VERSION.SDK_INT >= 33 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.preferential_network_service, "", R.drawable.globe_fill0) { navCtrl.navigate("PreferentialNetworkService") }
}
if(VERSION.SDK_INT >= 28 && deviceOwner) {
SubPageItem(R.string.override_apn_settings, "", R.drawable.cell_tower_fill0) { navCtrl.navigate("APN") }
}
@@ -232,12 +242,6 @@ private fun Switches() {
val deviceOwner = context.isDeviceOwner
Column(modifier = Modifier.fillMaxSize().padding(start = 20.dp, end = 16.dp)) {
Spacer(Modifier.padding(vertical = 5.dp))
if(VERSION.SDK_INT >= 33 && deviceOwner) {
SwitchItem(
R.string.preferential_network_service, "", R.drawable.globe_fill0,
{ dpm.isPreferentialNetworkServiceEnabled }, { dpm.isPreferentialNetworkServiceEnabled = it }, padding = false
)
}
if(VERSION.SDK_INT>=30 && (deviceOwner || dpm.isOrgProfile(receiver))) {
SwitchItem(R.string.lockdown_admin_configured_network, "", R.drawable.wifi_password_fill0,
{ dpm.hasLockdownAdminConfiguredNetworks(receiver) }, { dpm.setConfiguredNetworksLockdownState(receiver,it) }, padding = false
@@ -453,9 +457,11 @@ private fun PrivateDNS() {
try {
result = dpm.setGlobalPrivateDnsModeSpecifiedHost(receiver,inputHost)
Toast.makeText(context, operationResult[result], Toast.LENGTH_SHORT).show()
} catch(e:IllegalArgumentException) {
} catch(e: IllegalArgumentException) {
e.printStackTrace()
Toast.makeText(context, R.string.invalid_hostname, Toast.LENGTH_SHORT).show()
} catch(e:SecurityException) {
} catch(e: SecurityException) {
e.printStackTrace()
Toast.makeText(context, R.string.security_exception, Toast.LENGTH_SHORT).show()
} finally {
status = dnsStatus[dpm.getGlobalPrivateDnsMode(receiver)]
@@ -492,9 +498,11 @@ fun AlwaysOnVPNPackage(navCtrl: NavHostController) {
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
true
} catch(e: UnsupportedOperationException) {
e.printStackTrace()
Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show()
false
} catch(e: NameNotFoundException) {
e.printStackTrace()
Toast.makeText(context, R.string.not_installed, Toast.LENGTH_SHORT).show()
false
}
@@ -609,6 +617,7 @@ private fun RecommendedGlobalProxy() {
try {
port = proxyPort.toInt()
} catch(e: NumberFormatException) {
e.printStackTrace()
Toast.makeText(context, R.string.invalid_config, Toast.LENGTH_SHORT).show()
return@Button
}
@@ -702,7 +711,12 @@ private fun WifiAuthKeypair() {
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.padding(vertical = 5.dp))
val isExist = try{ dpm.isKeyPairGrantedToWifiAuth(keyPair) }catch(e:java.lang.IllegalArgumentException) { false }
val isExist = try {
dpm.isKeyPairGrantedToWifiAuth(keyPair)
} catch(e: java.lang.IllegalArgumentException) {
e.printStackTrace()
false
}
Text(stringResource(R.string.already_exist)+"$isExist")
Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
@@ -728,6 +742,166 @@ private fun WifiAuthKeypair() {
}
}
@SuppressLint("NewApi")
@Composable
fun PreferentialNetworkService() {
val focusMgr = LocalFocusManager.current
val context = LocalContext.current
val dpm = context.getDPM()
var masterEnabled by remember { mutableStateOf(false) }
val configs = remember { mutableStateListOf<PreferentialNetworkServiceConfig>() }
var index by remember { mutableIntStateOf(-1) }
var enabled by remember { mutableStateOf(false) }
var networkId by remember { mutableStateOf("") }
var allowFallback by remember { mutableStateOf(false) }
var blockNonMatching by remember { mutableStateOf(false) }
var excludedUids by remember { mutableStateOf("") }
var includedUids by remember { mutableStateOf("") }
fun refresh() {
val config = configs.getOrNull(index)
enabled = config?.isEnabled == true
networkId = config?.networkId?.toString() ?: ""
allowFallback = config?.isFallbackToDefaultConnectionAllowed == true
if(VERSION.SDK_INT >= 34) blockNonMatching = config?.shouldBlockNonMatchingNetworks() == true
includedUids = config?.includedUids?.joinToString("\n") ?: ""
excludedUids = config?.excludedUids?.joinToString("\n") ?: ""
}
fun saveCurrentConfig() {
val builder = PreferentialNetworkServiceConfig.Builder()
builder.setEnabled(enabled)
builder.setNetworkId(networkId.toInt())
builder.setFallbackToDefaultConnectionAllowed(allowFallback)
if(VERSION.SDK_INT >= 34) builder.setShouldBlockNonMatchingNetworks(blockNonMatching)
builder.setIncludedUids(includedUids.lines().dropWhile { it == "" }.map { it.toInt() }.toIntArray())
builder.setExcludedUids(excludedUids.lines().dropWhile { it == "" }.map { it.toInt() }.toIntArray())
if(index < configs.size) configs[index] = builder.build() else configs += builder.build()
}
fun initialize() {
masterEnabled = dpm.isPreferentialNetworkServiceEnabled
configs.addAll(dpm.preferentialNetworkServiceConfigs)
index = max(0, configs.size - 1)
refresh()
}
LaunchedEffect(Unit) { initialize() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.preferential_network_service), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
SwitchItem(
title = R.string.enabled, desc = "", icon = null,
state = masterEnabled, onCheckedChange = { masterEnabled = it }, padding = false
)
Row(
horizontalArrangement = Arrangement.SpaceAround,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
) {
IconButton(
onClick = {
try {
saveCurrentConfig()
index -= 1
refresh()
} catch(e: Exception) {
e.printStackTrace()
Toast.makeText(context, R.string.failed_to_save_current_config, Toast.LENGTH_SHORT).show()
}
},
enabled = index > 0
) {
Icon(imageVector = Icons.AutoMirrored.Default.KeyboardArrowLeft, contentDescription = stringResource(R.string.previous))
}
Text("${index + 1} / ${configs.size}")
IconButton(
onClick = {
try {
saveCurrentConfig()
index += 1
refresh()
} catch(e: Exception) {
e.printStackTrace()
Toast.makeText(context, R.string.failed_to_save_current_config, Toast.LENGTH_SHORT).show()
}
}
) {
Icon(
imageVector = if(index + 1 >= configs.size) Icons.Default.Add else Icons.AutoMirrored.Default.KeyboardArrowRight,
contentDescription = stringResource(R.string.previous)
)
}
}
Row {
Button(
onClick = {
try {
saveCurrentConfig()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
} catch(e: Exception) {
e.printStackTrace()
Toast.makeText(context, R.string.failed_to_save_current_config, Toast.LENGTH_SHORT).show()
}
},
modifier = Modifier.fillMaxWidth(0.49F)
) {
Text(stringResource(R.string.save_current_config))
}
Button(
onClick = {
if(index < configs.size) configs.removeAt(index)
if(index > 0) index -= 1
refresh()
},
modifier = Modifier.fillMaxWidth(0.96F)
) {
Text(stringResource(R.string.delete_current_config))
}
}
SwitchItem(
title = R.string.enabled, desc = "", icon = null,
state = enabled, onCheckedChange = { enabled = it }, padding = false
)
OutlinedTextField(
value = networkId, onValueChange = { networkId = it },
label = { Text(stringResource(R.string.network_id)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions { focusMgr.clearFocus() },
modifier = Modifier.fillMaxWidth().padding(bottom = 6.dp)
)
SwitchItem(
title = R.string.allow_fallback_to_default_connection, desc = "", icon = null,
state = allowFallback, onCheckedChange = { allowFallback = it }, padding = false
)
if(VERSION.SDK_INT >= 34) SwitchItem(
title = R.string.block_non_matching_networks, desc = "", icon = null,
state = blockNonMatching, onCheckedChange = { blockNonMatching = it }, padding = false
)
OutlinedTextField(
value = includedUids, onValueChange = { includedUids = it }, minLines = 2,
label = { Text(stringResource(R.string.included_uids)) },
supportingText = { Text(stringResource(R.string.one_uid_per_line)) },
modifier = Modifier.fillMaxWidth().padding(bottom = 6.dp)
)
OutlinedTextField(
value = excludedUids, onValueChange = { excludedUids = it }, minLines = 2,
label = { Text(stringResource(R.string.excluded_uids)) },
supportingText = { Text(stringResource(R.string.one_uid_per_line)) },
modifier = Modifier.fillMaxWidth().padding(bottom = 6.dp)
)
Button(
onClick = {
dpm.isPreferentialNetworkServiceEnabled = masterEnabled
dpm.preferentialNetworkServiceConfigs = configs
initialize()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
},
modifier = Modifier.fillMaxWidth().padding(top = 12.dp)
) {
Text(stringResource(R.string.apply))
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@SuppressLint("NewApi")
@Composable
private fun APN() {
@@ -747,7 +921,7 @@ private fun APN() {
Spacer(Modifier.padding(vertical = 5.dp))
SwitchItem(R.string.enable, "", null, { dpm.isOverrideApnEnabled(receiver) }, { dpm.setOverrideApnsEnabled(receiver,it) }, padding = false)
Text(text = stringResource(R.string.total_apn_amount, setting.size))
if(setting.size>0) {
if(setting.isNotEmpty()) {
Text(text = stringResource(R.string.select_a_apn_or_create, setting.size))
TextField(
value = inputNum,

View File

@@ -22,8 +22,6 @@ import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
@@ -78,7 +76,7 @@ fun DpmPermissions(navCtrl:NavHostController) {
composable(route = "DisableAccountManagement") { DisableAccountManagement() }
composable(route = "LockScreenInfo") { LockScreenInfo() }
composable(route = "SupportMsg") { SupportMsg() }
composable(route = "TransformOwnership") { TransformOwnership() }
composable(route = "TransformOwnership") { TransferOwnership() }
}
}
}
@@ -640,40 +638,32 @@ private fun DisableAccountManagement() {
@SuppressLint("NewApi")
@Composable
private fun TransformOwnership() {
private fun TransferOwnership() {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
val focusRequester = FocusRequester()
var component by remember { mutableStateOf("") }
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) {
var pkg by remember { mutableStateOf("") }
var cls by remember { mutableStateOf("") }
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.transfer_ownership), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
Text(text = stringResource(R.string.transfer_ownership_desc))
Spacer(Modifier.padding(vertical = 5.dp))
OutlinedTextField(
value = pkg, onValueChange = { pkg = it }, label = { Text(stringResource(R.string.target_package_name)) },
value = component, onValueChange = { component = it }, label = { Text(stringResource(R.string.target_component_name)) },
modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(onNext = { focusRequester.requestFocus() })
)
Spacer(Modifier.padding(vertical = 2.dp))
OutlinedTextField(
value = cls, onValueChange = {cls = it }, label = { Text(stringResource(R.string.target_class_name)) },
modifier = Modifier.focusRequester(focusRequester).fillMaxWidth(),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() })
keyboardActions = KeyboardActions(onNext = { focusMgr.clearFocus() })
)
Spacer(Modifier.padding(vertical = 5.dp))
Button(
onClick = {
val dpm = context.getDPM()
val receiver = context.getReceiver()
try {
dpm.transferOwnership(receiver, ComponentName(pkg, cls),null)
dpm.transferOwnership(receiver, ComponentName.unflattenFromString(component)!!, null)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
} catch(_:IllegalArgumentException) {
} catch(e: Exception) {
e.printStackTrace()
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
}
},

View File

@@ -57,6 +57,8 @@
<string name="delete">Delete</string> <!--TODO-->
<string name="yes">Yes</string> <!--TODO-->
<string name="no">No</string> <!--TODO-->
<string name="previous">Previous</string> <!--TODO-->
<string name="next">Next</string> <!--TODO-->
<!--Permissions-->
<string name="click_to_activate">Etkinleştirmek İçin Tıklayın</string>
@@ -86,8 +88,7 @@
<string name="account_type">Hesap Türleri</string>
<string name="transfer_ownership">Sahipliği Devret</string>
<string name="transfer_ownership_desc">Cihaz sahibi veya profil sahibi ayrıcalığını başka bir uygulamaya devredin.</string>
<string name="target_package_name">Hedef Paket Adı</string>
<string name="target_class_name">Hedef Sınıf Adı</string>
<string name="target_component_name">Target component name</string> <!--TODO-->
<string name="device_owner_lock_screen_info">Ekran Kilidi Bilgisi</string>
<string name="support_msg">Destek Mesajı</string>
<string name="short_support_msg">Kısa Mesaj</string>
@@ -219,7 +220,6 @@
<string name="wifi_mac_addr">Wi-Fi MAC adresi</string>
<string name="min_wifi_security_level">Minimum Wi-Fi güvenlik seviyesi</string>
<string name="wifi_security_open">ık</string>
<string name="preferential_network_service">Tercihli ağ hizmeti</string>
<string name="lockdown_admin_configured_network">Yönetici tarafından yapılandırılmış ağı kilitle</string>
<string name="wifi_ssid_policy">WiFi SSID politikası</string>
<string name="ssid_list_is">SSID listesi:</string>
@@ -246,6 +246,16 @@
<string name="export_logs">Export logs</string> <!--TODO-->
<string name="wifi_auth_keypair">WiFi anahtar çifti</string>
<string name="keypair">Anahtar çifti</string>
<string name="preferential_network_service">Tercihli ağ hizmeti</string>
<string name="network_id">Network ID</string> <!--TODO-->
<string name="allow_fallback_to_default_connection">Allow fallback to default connection</string> <!--TODO-->
<string name="block_non_matching_networks">Block non matching networks</string> <!--TODO-->
<string name="included_uids">Included UIDs</string> <!--TODO-->
<string name="excluded_uids">Excluded UIDs</string> <!--TODO-->
<string name="one_uid_per_line">One UID per line</string> <!--TODO-->
<string name="failed_to_save_current_config">Failed to save current config</string> <!--TODO-->
<string name="save_current_config">Save current config</string> <!--TODO-->
<string name="delete_current_config">Delete current config</string> <!--TODO-->
<string name="override_apn_settings">APN ayarlarını geçersiz kıl</string>
<string name="total_apn_amount">APN ayarlarının toplamı: %1$s</string>
<string name="select_a_apn_or_create">Düzenlemek istediğiniz APN ayarını seçin (1~%1$s) veya yeni bir APN ayarı oluşturmak için 0 girin.</string>

View File

@@ -54,6 +54,8 @@
<string name="delete">删除</string>
<string name="yes"></string>
<string name="no"></string>
<string name="previous">上一个</string>
<string name="next">下一个</string>
<!--Permissions-->
<string name="click_to_activate">点击以激活</string>
@@ -81,8 +83,7 @@
<string name="account_type">账号类型</string>
<string name="transfer_ownership">转移所有权</string>
<string name="transfer_ownership_desc">把Device owner或Profile owner权限转移到另一个应用</string>
<string name="target_package_name">目标</string>
<string name="target_class_name">目标类名</string>
<string name="target_component_name">目标组件</string>
<string name="device_owner_lock_screen_info">锁屏提示信息</string>
<string name="support_msg">提供支持的消息</string>
<string name="short_support_msg">提供支持的短消息</string>
@@ -214,7 +215,6 @@
<string name="wifi_mac_addr">Wi-Fi Mac地址</string>
<string name="min_wifi_security_level">最低WiFi安全等级</string>
<string name="wifi_security_open">开放</string>
<string name="preferential_network_service">优先网络服务</string>
<string name="lockdown_admin_configured_network">锁定由管理员配置的网络</string>
<string name="wifi_ssid_policy">WiFi SSID策略</string>
<string name="ssid_list_is">SSID列表</string>
@@ -241,6 +241,16 @@
<string name="export_logs">导出日志</string>
<string name="wifi_auth_keypair">WiFi密钥对</string>
<string name="keypair">密钥对</string>
<string name="preferential_network_service">首选网络服务</string>
<string name="network_id">网络ID</string>
<string name="allow_fallback_to_default_connection">允许回落到默认连接</string>
<string name="block_non_matching_networks">阻止不匹配的网络</string>
<string name="included_uids">包含的UID</string>
<string name="excluded_uids">排除的UIDs</string>
<string name="one_uid_per_line">一行一个UID</string>
<string name="failed_to_save_current_config">保存当前配置失败</string>
<string name="save_current_config">保存当前配置</string>
<string name="delete_current_config">删除当前配置</string>
<string name="override_apn_settings">APN设置</string>
<string name="total_apn_amount">一共有%1$s个APN设置</string>
<string name="select_a_apn_or_create">选择一个你要修改的APN设置(1~%1$s)或者输入0以新建APN设置</string>

View File

@@ -57,6 +57,8 @@
<string name="delete">Delete</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="previous">Previous</string>
<string name="next">Next</string>
<!--Permissions-->
<string name="click_to_activate">Click to activate</string>
@@ -87,8 +89,7 @@
<string name="account_type">Account type</string>
<string name="transfer_ownership">Transfer Ownership</string>
<string name="transfer_ownership_desc">Transfer device owner or profile owner privilege to another app.</string>
<string name="target_package_name">Target package name</string>
<string name="target_class_name">Target class name</string>
<string name="target_component_name">Target component name</string>
<string name="device_owner_lock_screen_info">Lockscreen info</string>
<string name="support_msg">Support Message</string>
<string name="short_support_msg">Short message</string>
@@ -223,7 +224,6 @@
<string name="wifi_mac_addr">Wi-Fi Mac address</string>
<string name="min_wifi_security_level">Min Wi-Fi security level</string>
<string name="wifi_security_open">Open</string>
<string name="preferential_network_service">Preferential network service</string>
<string name="lockdown_admin_configured_network">Lockdown admin configured network</string>
<string name="wifi_ssid_policy">WiFi SSID policy</string>
<string name="ssid_list_is">SSID list:</string>
@@ -250,6 +250,16 @@
<string name="export_logs">Export logs</string>
<string name="wifi_auth_keypair">WiFi keypair</string>
<string name="keypair">Keypair</string>
<string name="preferential_network_service">Preferential network service</string>
<string name="network_id">Network ID</string>
<string name="allow_fallback_to_default_connection">Allow fallback to default connection</string>
<string name="block_non_matching_networks">Block non matching networks</string>
<string name="included_uids">Included UIDs</string>
<string name="excluded_uids">Excluded UIDs</string>
<string name="one_uid_per_line">One UID per line</string>
<string name="failed_to_save_current_config">Failed to save current config</string>
<string name="save_current_config">Save current config</string>
<string name="delete_current_config">Delete current config</string>
<string name="override_apn_settings">APN settings</string>
<string name="total_apn_amount">APN settings amount: %1$s</string>
<string name="select_a_apn_or_create">Select an APN setting you want to edit (1~%1$s) or enter 0 to create a new APN setting. </string>