mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 19:15:58 +00:00
New APN settings screen
This commit is contained in:
@@ -62,6 +62,8 @@ import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.toRoute
|
||||
import com.bintianqi.owndroid.dpm.Accounts
|
||||
import com.bintianqi.owndroid.dpm.AccountsScreen
|
||||
import com.bintianqi.owndroid.dpm.AddApnSetting
|
||||
import com.bintianqi.owndroid.dpm.AddApnSettingScreen
|
||||
import com.bintianqi.owndroid.dpm.AddDelegatedAdmin
|
||||
import com.bintianqi.owndroid.dpm.AddDelegatedAdminScreen
|
||||
import com.bintianqi.owndroid.dpm.AddNetwork
|
||||
@@ -285,10 +287,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
composable<Home> { HomeScreen { navController.navigate(it) } }
|
||||
|
||||
composable<Permissions> {
|
||||
PermissionsScreen(::navigateUp, { navController.navigate(it) }) {
|
||||
val dest = navController.graph.findNode(ShizukuScreen)!!.id
|
||||
navController.navigate(dest, it)
|
||||
}
|
||||
PermissionsScreen(::navigateUp, { navController.navigate(it) }) { navController.navigate(ShizukuScreen, it) }
|
||||
}
|
||||
composable<ShizukuScreen> { ShizukuScreen(it.arguments!!, ::navigateUp) { navController.navigate(it) } }
|
||||
composable<Accounts>(mapOf(serializableNavTypePair<List<Accounts.Account>>())) { AccountsScreen(it.toRoute(), ::navigateUp) }
|
||||
@@ -323,12 +322,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
composable<WipeData> { WipeDataScreen(::navigateUp) }
|
||||
|
||||
composable<Network> { NetworkScreen(::navigateUp, ::navigate) }
|
||||
composable<WiFi> {
|
||||
WifiScreen(::navigateUp, { navController.navigate(it) }) {
|
||||
val dest = navController.graph.findNode(AddNetwork)!!.id
|
||||
navController.navigate(dest, it)
|
||||
}
|
||||
}
|
||||
composable<WiFi> { WifiScreen(::navigateUp, { navController.navigate(it) }) { navController.navigate(AddNetwork, it)} }
|
||||
composable<NetworkOptions> { NetworkOptionsScreen(::navigateUp) }
|
||||
composable<AddNetwork> { AddNetworkScreen(it.arguments!!, ::navigateUp) }
|
||||
composable<WifiSecurityLevel> { WifiSecurityLevelScreen(::navigateUp) }
|
||||
@@ -344,7 +338,8 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
composable<WifiAuthKeypair> { WifiAuthKeypairScreen(::navigateUp) }
|
||||
composable<PreferentialNetworkService> { PreferentialNetworkServiceScreen(::navigateUp, ::navigate) }
|
||||
composable<AddPreferentialNetworkServiceConfig> { AddPreferentialNetworkServiceConfigScreen(it.toRoute(), ::navigateUp) }
|
||||
composable<OverrideApn> { OverrideApnScreen(::navigateUp) }
|
||||
composable<OverrideApn> { OverrideApnScreen(::navigateUp) { navController.navigate(AddApnSetting, it) } }
|
||||
composable<AddApnSetting> { AddApnSettingScreen(it.arguments?.getParcelable("setting"), ::navigateUp) }
|
||||
|
||||
composable<WorkProfile> { WorkProfileScreen(::navigateUp, ::navigate) }
|
||||
composable<OrganizationOwnedProfile> { OrganizationOwnedProfileScreen(::navigateUp) }
|
||||
|
||||
@@ -17,6 +17,7 @@ import androidx.activity.result.contract.ActivityResultContract
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.NavType
|
||||
import com.bintianqi.owndroid.dpm.addDeviceAdmin
|
||||
import kotlinx.serialization.encodeToString
|
||||
@@ -135,3 +136,7 @@ fun exportLogs(context: Context, uri: Uri) {
|
||||
context.showOperationResultToast(proc.exitValue() == 0)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> NavHostController.navigate(route: T, args: Bundle) {
|
||||
navigate(graph.findNode(route)!!.id, args)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OFF
|
||||
import android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OPPORTUNISTIC
|
||||
@@ -32,23 +33,7 @@ import android.net.wifi.WifiManager
|
||||
import android.net.wifi.WifiSsid
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Bundle
|
||||
import android.telephony.TelephonyManager
|
||||
import android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID
|
||||
import android.telephony.data.ApnSetting.AUTH_TYPE_CHAP
|
||||
import android.telephony.data.ApnSetting.AUTH_TYPE_NONE
|
||||
import android.telephony.data.ApnSetting.AUTH_TYPE_PAP
|
||||
import android.telephony.data.ApnSetting.AUTH_TYPE_PAP_OR_CHAP
|
||||
import android.telephony.data.ApnSetting.Builder
|
||||
import android.telephony.data.ApnSetting.MVNO_TYPE_GID
|
||||
import android.telephony.data.ApnSetting.MVNO_TYPE_ICCID
|
||||
import android.telephony.data.ApnSetting.MVNO_TYPE_IMSI
|
||||
import android.telephony.data.ApnSetting.MVNO_TYPE_SPN
|
||||
import android.telephony.data.ApnSetting.PROTOCOL_IP
|
||||
import android.telephony.data.ApnSetting.PROTOCOL_IPV4V6
|
||||
import android.telephony.data.ApnSetting.PROTOCOL_IPV6
|
||||
import android.telephony.data.ApnSetting.PROTOCOL_NON_IP
|
||||
import android.telephony.data.ApnSetting.PROTOCOL_PPP
|
||||
import android.telephony.data.ApnSetting.PROTOCOL_UNSTRUCTURED
|
||||
import android.telephony.data.ApnSetting
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
@@ -56,6 +41,7 @@ import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
@@ -63,6 +49,8 @@ import androidx.compose.foundation.interaction.collectIsPressedAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@@ -80,6 +68,7 @@ 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.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.LocationOn
|
||||
@@ -88,9 +77,11 @@ import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.DatePicker
|
||||
import androidx.compose.material3.DatePickerDialog
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -106,7 +97,6 @@ import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -123,6 +113,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -132,7 +123,6 @@ 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.core.net.toUri
|
||||
import androidx.core.os.bundleOf
|
||||
import com.bintianqi.owndroid.ChoosePackageContract
|
||||
import com.bintianqi.owndroid.R
|
||||
@@ -196,7 +186,7 @@ fun NetworkScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
||||
FunctionItem(R.string.preferential_network_service, icon = R.drawable.globe_fill0) { onNavigate(PreferentialNetworkService) }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28 && deviceOwner) {
|
||||
FunctionItem(R.string.override_apn_settings, icon = R.drawable.cell_tower_fill0) { onNavigate(OverrideApn) }
|
||||
FunctionItem(R.string.override_apn, icon = R.drawable.cell_tower_fill0) { onNavigate(OverrideApn) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1880,374 +1870,397 @@ fun AddPreferentialNetworkServiceConfigScreen(route: AddPreferentialNetworkServi
|
||||
|
||||
@RequiresApi(28)
|
||||
@Composable
|
||||
fun OverrideApnScreen(onNavigateUp: () -> Unit) {
|
||||
fun OverrideApnScreen(onNavigateUp: () -> Unit, onNavigateToAddSetting: (Bundle) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
val receiver = context.getReceiver()
|
||||
val focusMgr = LocalFocusManager.current
|
||||
val setting = dpm.getOverrideApns(receiver)
|
||||
var inputNum by remember { mutableStateOf("0") }
|
||||
var nextStep by remember { mutableStateOf(false) }
|
||||
val builder = Builder()
|
||||
MyScaffold(R.string.override_apn_settings, 8.dp, onNavigateUp) {
|
||||
Text(text = stringResource(id = R.string.developing))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
var enabled by remember { mutableStateOf(false) }
|
||||
val settings = remember { mutableStateListOf<ApnSetting>() }
|
||||
fun refresh() {
|
||||
enabled = dpm.isOverrideApnEnabled(receiver)
|
||||
settings.clear()
|
||||
settings.addAll(dpm.getOverrideApns(receiver))
|
||||
}
|
||||
LaunchedEffect(Unit) { refresh() }
|
||||
MyScaffold(R.string.override_apn, 0.dp, onNavigateUp, false) {
|
||||
SwitchItem(
|
||||
R.string.enable,
|
||||
getState = { dpm.isOverrideApnEnabled(receiver) }, onCheckedChange = { dpm.setOverrideApnsEnabled(receiver,it) },
|
||||
padding = false
|
||||
R.string.enable, state = enabled,
|
||||
onCheckedChange = {
|
||||
dpm.setOverrideApnsEnabled(receiver, it)
|
||||
refresh()
|
||||
}
|
||||
)
|
||||
Text(text = stringResource(R.string.total_apn_amount, setting.size))
|
||||
if(setting.isNotEmpty()) {
|
||||
Text(text = stringResource(R.string.select_a_apn_or_create, setting.size))
|
||||
TextField(
|
||||
value = inputNum,
|
||||
label = { Text("APN") },
|
||||
onValueChange = { inputNum = it },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp),
|
||||
enabled = !nextStep
|
||||
)
|
||||
}else{
|
||||
Text(text = stringResource(R.string.no_apn_you_should_create_one))
|
||||
settings.forEach {
|
||||
Row(
|
||||
Modifier.fillMaxWidth().padding(start = 16.dp, end = 8.dp, top = 8.dp, bottom = 8.dp),
|
||||
Arrangement.SpaceBetween, Alignment.CenterVertically
|
||||
) {
|
||||
Column {
|
||||
Text(it.id.toString())
|
||||
Text(it.apnName.toString(), color = MaterialTheme.colorScheme.onSurfaceVariant, style = typography.bodyMedium)
|
||||
Text(it.entryName.toString(), color = MaterialTheme.colorScheme.onSurfaceVariant, style = typography.bodyMedium)
|
||||
}
|
||||
IconButton({
|
||||
onNavigateToAddSetting(bundleOf("setting" to it))
|
||||
}) {
|
||||
Icon(Icons.Default.Edit, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = { focusMgr.clearFocus(); nextStep =! nextStep },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = inputNum != "" && (nextStep || inputNum=="0" || setting[inputNum.toInt()-1] != null)
|
||||
Row(
|
||||
Modifier.fillMaxWidth().clickable {
|
||||
onNavigateToAddSetting(Bundle())
|
||||
}.padding(horizontal = 8.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(stringResource(if(nextStep) R.string.previous_step else R.string.next_step))
|
||||
}
|
||||
var result = Builder().build()
|
||||
AnimatedVisibility(nextStep) {
|
||||
var carrierEnabled by remember { mutableStateOf(false) }
|
||||
var inputApnName by remember { mutableStateOf("") }
|
||||
var user by remember { mutableStateOf("") }
|
||||
var profileId by remember { mutableStateOf("") }
|
||||
var selectedAuthType by remember { mutableIntStateOf(AUTH_TYPE_NONE) }
|
||||
var carrierId by remember { mutableStateOf("$UNKNOWN_CARRIER_ID") }
|
||||
var apnTypeBitmask by remember { mutableStateOf("") }
|
||||
var entryName by remember { mutableStateOf("") }
|
||||
var mmsProxyAddress by remember { mutableStateOf("") }
|
||||
var mmsProxyPort by remember { mutableStateOf("") }
|
||||
var proxyAddress by remember { mutableStateOf("") }
|
||||
var proxyPort by remember { mutableStateOf("") }
|
||||
var mmsc by remember { mutableStateOf("") }
|
||||
var mtuV4 by remember { mutableStateOf("") }
|
||||
var mtuV6 by remember { mutableStateOf("") }
|
||||
var mvnoType by remember { mutableIntStateOf(-1) }
|
||||
var networkTypeBitmask by remember { mutableStateOf("") }
|
||||
var operatorNumeric by remember { mutableStateOf("") }
|
||||
var password by remember { mutableStateOf("") }
|
||||
var persistent by remember { mutableStateOf(false) }
|
||||
var protocol by remember { mutableIntStateOf(-1) }
|
||||
var roamingProtocol by remember { mutableIntStateOf(-1) }
|
||||
var id by remember { mutableIntStateOf(0) }
|
||||
|
||||
if(inputNum!="0") {
|
||||
val current = setting[inputNum.toInt()-1]
|
||||
id = current.id
|
||||
carrierEnabled = current.isEnabled
|
||||
inputApnName = current.apnName
|
||||
user = current.user
|
||||
if(VERSION.SDK_INT>=33) {profileId = current.profileId.toString() }
|
||||
selectedAuthType = current.authType
|
||||
apnTypeBitmask = current.apnTypeBitmask.toString()
|
||||
entryName = current.entryName
|
||||
if(VERSION.SDK_INT>=29) {mmsProxyAddress = current.mmsProxyAddressAsString}
|
||||
mmsProxyPort = current.mmsProxyPort.toString()
|
||||
if(VERSION.SDK_INT>=29) {proxyAddress = current.proxyAddressAsString}
|
||||
proxyPort = current.proxyPort.toString()
|
||||
mmsc = current.mmsc.toString()
|
||||
if(VERSION.SDK_INT>=33) { mtuV4 = current.mtuV4.toString(); mtuV6 = current.mtuV6.toString() }
|
||||
mvnoType = current.mvnoType
|
||||
networkTypeBitmask = current.networkTypeBitmask.toString()
|
||||
operatorNumeric = current.operatorNumeric
|
||||
password = current.password
|
||||
if(VERSION.SDK_INT>=33) {persistent = current.isPersistent}
|
||||
protocol = current.protocol
|
||||
roamingProtocol = current.roamingProtocol
|
||||
}
|
||||
|
||||
Column {
|
||||
|
||||
Text(text = "APN", style = typography.titleLarge)
|
||||
TextField(
|
||||
value = inputApnName,
|
||||
onValueChange = {inputApnName=it },
|
||||
label = { Text(stringResource(R.string.name)) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(text = stringResource(R.string.enable), style = typography.titleLarge)
|
||||
Switch(checked = carrierEnabled, onCheckedChange = {carrierEnabled=it })
|
||||
}
|
||||
|
||||
Text(text = stringResource(R.string.user_name), style = typography.titleLarge)
|
||||
TextField(
|
||||
value = user,
|
||||
onValueChange = { user=it },
|
||||
label = { Text(stringResource(R.string.user_name)) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
if(VERSION.SDK_INT>=33) {
|
||||
Text(text = stringResource(R.string.profile_id), style = typography.titleLarge)
|
||||
TextField(
|
||||
value = profileId,
|
||||
onValueChange = { profileId=it },
|
||||
label = { Text("ID") },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Text(text = stringResource(R.string.auth_type), style = typography.titleLarge)
|
||||
RadioButtonItem(R.string.none, selectedAuthType==AUTH_TYPE_NONE) { selectedAuthType = AUTH_TYPE_NONE }
|
||||
RadioButtonItem("CHAP", selectedAuthType == AUTH_TYPE_CHAP) { selectedAuthType = AUTH_TYPE_CHAP }
|
||||
RadioButtonItem("PAP", selectedAuthType == AUTH_TYPE_PAP) { selectedAuthType = AUTH_TYPE_PAP }
|
||||
RadioButtonItem("PAP/CHAP", selectedAuthType == AUTH_TYPE_PAP_OR_CHAP) { selectedAuthType = AUTH_TYPE_PAP_OR_CHAP }
|
||||
|
||||
if(VERSION.SDK_INT>=29) {
|
||||
val ts = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
|
||||
carrierId = ts.simCarrierId.toString()
|
||||
Text(text = "CarrierID", style = typography.titleLarge)
|
||||
TextField(
|
||||
value = carrierId,
|
||||
onValueChange = { carrierId=it },
|
||||
label = { Text("ID") },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Text(text = stringResource(R.string.apn_type), style = typography.titleLarge)
|
||||
TextField(
|
||||
value = apnTypeBitmask,
|
||||
onValueChange = { apnTypeBitmask=it },
|
||||
label = { Text(stringResource(R.string.bitmask)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
Text(text = stringResource(R.string.description), style = typography.titleLarge)
|
||||
TextField(
|
||||
value = entryName,
|
||||
onValueChange = {entryName=it },
|
||||
label = { Text(stringResource(R.string.description)) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
Text(text = stringResource(R.string.mms_proxy), style = typography.titleLarge)
|
||||
if(VERSION.SDK_INT>=29) {
|
||||
TextField(
|
||||
value = mmsProxyAddress,
|
||||
onValueChange = { mmsProxyAddress=it },
|
||||
label = { Text(stringResource(R.string.address)) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
}
|
||||
TextField(
|
||||
value = mmsProxyPort,
|
||||
onValueChange = { mmsProxyPort=it },
|
||||
label = { Text(stringResource(R.string.port)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
Text(text = stringResource(R.string.proxy), style = typography.titleLarge)
|
||||
if(VERSION.SDK_INT>=29) {
|
||||
TextField(
|
||||
value = proxyAddress,
|
||||
onValueChange = { proxyAddress=it },
|
||||
label = { Text(stringResource(R.string.address)) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
}
|
||||
TextField(
|
||||
value = proxyPort,
|
||||
onValueChange = { proxyPort=it },
|
||||
label = { Text(stringResource(R.string.port)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
Text(text = "MMSC", style = typography.titleLarge)
|
||||
TextField(
|
||||
value = mmsc,
|
||||
onValueChange = { mmsc=it },
|
||||
label = { Text("Uri") },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
if(VERSION.SDK_INT>=33) {
|
||||
Text(text = "MTU", style = typography.titleLarge)
|
||||
TextField(
|
||||
value = mtuV4,
|
||||
onValueChange = { mtuV4=it },
|
||||
label = { Text("IPV4") },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
TextField(
|
||||
value = mtuV6,
|
||||
onValueChange = { mtuV6=it },
|
||||
label = { Text("IPV6") },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Text(text = "MVNO", style = typography.titleLarge)
|
||||
RadioButtonItem("SPN", mvnoType == MVNO_TYPE_SPN) { mvnoType = MVNO_TYPE_SPN }
|
||||
RadioButtonItem("IMSI", mvnoType == MVNO_TYPE_IMSI) { mvnoType = MVNO_TYPE_IMSI }
|
||||
RadioButtonItem("GID", mvnoType == MVNO_TYPE_GID) { mvnoType = MVNO_TYPE_GID }
|
||||
RadioButtonItem("ICCID", mvnoType == MVNO_TYPE_ICCID) { mvnoType = MVNO_TYPE_ICCID }
|
||||
|
||||
Text(text = stringResource(R.string.apn_network_type), style = typography.titleLarge)
|
||||
TextField(
|
||||
value = networkTypeBitmask,
|
||||
onValueChange = { networkTypeBitmask=it },
|
||||
label = { Text(stringResource(R.string.bitmask)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
Text(text = "OperatorNumeric", style = typography.titleLarge)
|
||||
TextField(
|
||||
value = operatorNumeric,
|
||||
onValueChange = { operatorNumeric=it },
|
||||
label = { Text("ID") },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
Text(text = stringResource(R.string.password), style = typography.titleLarge)
|
||||
TextField(
|
||||
value = password,
|
||||
onValueChange = { password=it },
|
||||
label = { Text(stringResource(R.string.password)) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 4.dp)
|
||||
)
|
||||
|
||||
if(VERSION.SDK_INT>=33) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(text = stringResource(R.string.persistent), style = typography.titleLarge)
|
||||
Switch(checked = persistent, onCheckedChange = { persistent=it })
|
||||
}
|
||||
}
|
||||
|
||||
Text(text = stringResource(R.string.protocol), style = typography.titleLarge)
|
||||
RadioButtonItem("IPV4", protocol == PROTOCOL_IP) { protocol = PROTOCOL_IP }
|
||||
RadioButtonItem("IPV6", protocol == PROTOCOL_IPV6) { protocol = PROTOCOL_IPV6 }
|
||||
RadioButtonItem("IPV4/IPV6", protocol == PROTOCOL_IPV4V6) { protocol = PROTOCOL_IPV4V6 }
|
||||
RadioButtonItem("PPP", protocol == PROTOCOL_PPP) { protocol = PROTOCOL_PPP }
|
||||
if(VERSION.SDK_INT>=29) {
|
||||
RadioButtonItem("non-IP", protocol == PROTOCOL_NON_IP) { protocol = PROTOCOL_NON_IP }
|
||||
RadioButtonItem("Unstructured", protocol == PROTOCOL_UNSTRUCTURED) { protocol = PROTOCOL_UNSTRUCTURED }
|
||||
}
|
||||
|
||||
Text(text = stringResource(R.string.roaming_protocol), style = typography.titleLarge)
|
||||
RadioButtonItem("IPV4", roamingProtocol == PROTOCOL_IP) { roamingProtocol = PROTOCOL_IP }
|
||||
RadioButtonItem("IPV6", roamingProtocol == PROTOCOL_IPV6) { roamingProtocol = PROTOCOL_IPV6 }
|
||||
RadioButtonItem("IPV4/IPV6", roamingProtocol == PROTOCOL_IPV4V6) { roamingProtocol = PROTOCOL_IPV4V6 }
|
||||
RadioButtonItem("PPP", roamingProtocol == PROTOCOL_PPP) { roamingProtocol = PROTOCOL_PPP }
|
||||
if(VERSION.SDK_INT>=29) {
|
||||
RadioButtonItem("non-IP", roamingProtocol == PROTOCOL_NON_IP) { roamingProtocol = PROTOCOL_NON_IP }
|
||||
RadioButtonItem("Unstructured", roamingProtocol == PROTOCOL_UNSTRUCTURED) { roamingProtocol = PROTOCOL_UNSTRUCTURED }
|
||||
}
|
||||
|
||||
var finalStep by remember { mutableStateOf(false) }
|
||||
Button(
|
||||
onClick = {
|
||||
if(!finalStep) {
|
||||
builder.setCarrierEnabled(carrierEnabled)
|
||||
builder.setApnName(inputApnName)
|
||||
builder.setUser(user)
|
||||
if(VERSION.SDK_INT>=33) { builder.setProfileId(profileId.toInt()) }
|
||||
builder.setAuthType(selectedAuthType)
|
||||
if(VERSION.SDK_INT>=29) { builder.setCarrierId(carrierId.toInt()) }
|
||||
builder.setApnTypeBitmask(apnTypeBitmask.toInt())
|
||||
builder.setEntryName(entryName)
|
||||
if(VERSION.SDK_INT>=29) { builder.setMmsProxyAddress(mmsProxyAddress) }
|
||||
builder.setMmsProxyPort(mmsProxyPort.toInt())
|
||||
if(VERSION.SDK_INT>=29) { builder.setProxyAddress(proxyAddress) }
|
||||
builder.setProxyPort(proxyPort.toInt())
|
||||
builder.setMmsc(mmsc.toUri())
|
||||
if(VERSION.SDK_INT>=33) { builder.setMtuV4(mtuV4.toInt()); builder.setMtuV6(mtuV6.toInt()) }
|
||||
builder.setMvnoType(mvnoType)
|
||||
builder.setNetworkTypeBitmask(networkTypeBitmask.toInt())
|
||||
builder.setOperatorNumeric(operatorNumeric)
|
||||
builder.setPassword(password)
|
||||
if(VERSION.SDK_INT>=33) { builder.setPersistent(persistent) }
|
||||
builder.setProtocol(protocol)
|
||||
builder.setRoamingProtocol(roamingProtocol)
|
||||
result = builder.build()
|
||||
}
|
||||
finalStep=!finalStep
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(if(finalStep) R.string.previous_step else R.string.next_step))
|
||||
}
|
||||
AnimatedVisibility(finalStep) {
|
||||
if(inputNum=="0") {
|
||||
Button(
|
||||
onClick = { dpm.addOverrideApn(receiver,result) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.create))
|
||||
}
|
||||
}else{
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Button(
|
||||
onClick = { context.showOperationResultToast(dpm.updateOverrideApn(receiver, id, result)) },
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.update))
|
||||
}
|
||||
Button(
|
||||
onClick = { context.showOperationResultToast(dpm.removeOverrideApn(receiver,id)) },
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Icon(Icons.Default.Add, null, Modifier.padding(horizontal = 8.dp))
|
||||
Text(stringResource(R.string.add_config), style = typography.labelLarge)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class ApnType(val id: Int, val name: String, val requiresApi: Int = 0)
|
||||
@SuppressLint("InlinedApi")
|
||||
private val apnTypes = listOf(
|
||||
ApnType(ApnSetting.TYPE_DEFAULT, "Default"), ApnType(ApnSetting.TYPE_MMS, "MMS"), ApnType(ApnSetting.TYPE_SUPL, "SUPL"),
|
||||
ApnType(ApnSetting.TYPE_DUN, "DUN"), ApnType(ApnSetting.TYPE_HIPRI, "HiPri"), ApnType(ApnSetting.TYPE_FOTA, "FOTA"),
|
||||
ApnType(ApnSetting.TYPE_IMS, "IMS"), ApnType(ApnSetting.TYPE_CBS, "CBS"), ApnType(ApnSetting.TYPE_IA, "IA"),
|
||||
ApnType(ApnSetting.TYPE_EMERGENCY, "Emergency"), ApnType(ApnSetting.TYPE_MCX, "MCX", 29), ApnType(ApnSetting.TYPE_XCAP, "XCAP", 30),
|
||||
ApnType(ApnSetting.TYPE_BIP, "BIP", 31), ApnType(ApnSetting.TYPE_VSIM, "VSIM", 31), ApnType(ApnSetting.TYPE_ENTERPRISE, "Enterprise", 33),
|
||||
ApnType(ApnSetting.TYPE_RCS, "RCS", 35) // TODO: Adapt A16 later
|
||||
)
|
||||
|
||||
@Serializable object AddApnSetting
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
|
||||
@RequiresApi(28)
|
||||
@Composable
|
||||
fun AddApnSettingScreen(origin: ApnSetting?, onNavigateUp: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
val receiver = context.getReceiver()
|
||||
val fm = LocalFocusManager.current
|
||||
var dropdown by remember { mutableIntStateOf(0) } // 1:Auth type, 2:MVNO type, 3:Protocol, 4:Roaming protocol
|
||||
var dialog by remember { mutableIntStateOf(0) } // 1:Proxy, 2:MMS proxy
|
||||
var enabled by remember { mutableStateOf(true) }
|
||||
var apnName by remember { mutableStateOf(origin?.apnName ?: "") }
|
||||
var entryName by remember { mutableStateOf(origin?.entryName ?: "") }
|
||||
var apnType by remember { mutableIntStateOf(origin?.apnTypeBitmask ?: 0) }
|
||||
var profileId by remember { mutableStateOf(if(VERSION.SDK_INT >= 33) origin?.profileId?.toString() ?: "" else "") }
|
||||
var carrierId by remember { mutableStateOf(if(VERSION.SDK_INT >= 29) origin?.carrierId?.toString() ?: "" else "") }
|
||||
var authType by remember { mutableIntStateOf(origin?.authType ?: ApnSetting.AUTH_TYPE_NONE) }
|
||||
var user by remember { mutableStateOf(origin?.user ?: "") }
|
||||
var password by remember { mutableStateOf(origin?.password ?: "") }
|
||||
var proxyAddress by remember { mutableStateOf(if(VERSION.SDK_INT >= 29) origin?.proxyAddressAsString ?: "" else "") }
|
||||
var proxyPort by remember { mutableStateOf(if(VERSION.SDK_INT >= 29) origin?.proxyPort?.toString() ?: "" else "") }
|
||||
var mmsProxyAddress by remember { mutableStateOf(if(VERSION.SDK_INT >= 29) origin?.mmsProxyAddressAsString ?: "" else "") }
|
||||
var mmsProxyPort by remember { mutableStateOf(if(VERSION.SDK_INT >= 29) origin?.mmsProxyPort?.toString() ?: "" else "") }
|
||||
var mmsc by remember { mutableStateOf(origin?.mmsc?.toString() ?: "") }
|
||||
var mtuV4 by remember { mutableStateOf(if(VERSION.SDK_INT >= 33) origin?.mtuV4?.toString() ?: "" else "") }
|
||||
var mtuV6 by remember { mutableStateOf(if(VERSION.SDK_INT >= 33) origin?.mtuV6?.toString() ?: "" else "") }
|
||||
var mvnoType by remember { mutableIntStateOf(origin?.mvnoType ?: ApnSetting.MVNO_TYPE_SPN) }
|
||||
var networkTypeBitmask by remember { mutableStateOf(origin?.networkTypeBitmask?.toString() ?: "") }
|
||||
var operatorNumeric by remember { mutableStateOf(origin?.operatorNumeric ?: "") }
|
||||
var protocol by remember { mutableIntStateOf(origin?.protocol ?: ApnSetting.PROTOCOL_IP) }
|
||||
var roamingProtocol by remember { mutableIntStateOf(origin?.roamingProtocol ?: ApnSetting.PROTOCOL_IP) }
|
||||
var persistent by remember { mutableStateOf(if(VERSION.SDK_INT >= 33) origin?.isPersistent == true else false) }
|
||||
var alwaysOn by remember { mutableStateOf(VERSION.SDK_INT >= 35 && origin?.isAlwaysOn == true) }
|
||||
var errorMessage: String? by remember { mutableStateOf(null) }
|
||||
MyScaffold(R.string.apn_setting, 8.dp, onNavigateUp, false) {
|
||||
val protocolMap = mapOf(
|
||||
ApnSetting.PROTOCOL_IP to "IPv4", ApnSetting.PROTOCOL_IPV6 to "IPv6",
|
||||
ApnSetting.PROTOCOL_IPV4V6 to "IPv4/v6", ApnSetting.PROTOCOL_PPP to "PPP"
|
||||
).let {
|
||||
if(VERSION.SDK_INT >= 29) {
|
||||
it.plus(listOf(ApnSetting.PROTOCOL_NON_IP to "Non-IP", ApnSetting.PROTOCOL_UNSTRUCTURED to "Unstructured"))
|
||||
} else it
|
||||
}
|
||||
SwitchItem(R.string.enabled, state = enabled, onCheckedChange = { enabled = it }, padding = false)
|
||||
OutlinedTextField(
|
||||
apnName, { apnName = it }, Modifier.fillMaxWidth(),
|
||||
label = { Text(stringResource(R.string.apn_name) + " (*)") },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() }
|
||||
)
|
||||
OutlinedTextField(
|
||||
entryName, { entryName = it }, Modifier.fillMaxWidth().padding(vertical = 4.dp),
|
||||
label = { Text(stringResource(R.string.entry_name) + " (*)") },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() }
|
||||
)
|
||||
Text(stringResource(R.string.type) + " (*)", Modifier.padding(vertical = 4.dp), style = typography.titleLarge)
|
||||
FlowRow(Modifier.padding(bottom = 4.dp)) {
|
||||
apnTypes.filter { VERSION.SDK_INT >= it.requiresApi }.forEach {
|
||||
FilterChip(
|
||||
apnType and it.id == it.id, {
|
||||
apnType = if(apnType and it.id == it.id) apnType and (apnType xor it.id) else apnType or it.id
|
||||
},
|
||||
{ Text(it.name) }, Modifier.padding(horizontal = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
if(VERSION.SDK_INT >= 33) OutlinedTextField(
|
||||
profileId, { profileId = it }, Modifier.fillMaxWidth(),
|
||||
label = { Text(stringResource(R.string.profile_id)) }, isError = profileId.isNotEmpty() && profileId.toIntOrNull() == null,
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() }
|
||||
)
|
||||
if(VERSION.SDK_INT >= 29) OutlinedTextField(
|
||||
carrierId, { carrierId = it }, Modifier.fillMaxWidth().padding(vertical = 4.dp),
|
||||
label = { Text(stringResource(R.string.carrier_id)) },
|
||||
isError = carrierId.isNotEmpty() && carrierId.toIntOrNull() == null,
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() }
|
||||
)
|
||||
Row(Modifier.fillMaxWidth().padding(vertical = 10.dp), Arrangement.SpaceBetween, Alignment.CenterVertically) {
|
||||
val rotate by animateFloatAsState(if(dropdown == 1) 180F else 0F)
|
||||
val authTypeMap = mapOf(
|
||||
ApnSetting.AUTH_TYPE_NONE to stringResource(R.string.none), ApnSetting.AUTH_TYPE_PAP to "PAP",
|
||||
ApnSetting.AUTH_TYPE_CHAP to "CHAP", ApnSetting.AUTH_TYPE_PAP_OR_CHAP to "PAP/CHAP"
|
||||
)
|
||||
Text(stringResource(R.string.auth_type))
|
||||
Row(Modifier.clickable { dropdown = 1 }.padding(4.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(authTypeMap[authType]!!, Modifier.padding(2.dp))
|
||||
Icon(Icons.Default.ArrowDropDown, null, Modifier.padding(start = 4.dp).rotate(rotate))
|
||||
DropdownMenu(dropdown == 1, { dropdown = 0 }) {
|
||||
authTypeMap.forEach {
|
||||
DropdownMenuItem({ Text(it.value) }, { authType = it.key; dropdown = 0 })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
OutlinedTextField(
|
||||
user, { user = it }, Modifier.fillMaxWidth(),
|
||||
label = { Text(stringResource(R.string.user)) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() }
|
||||
)
|
||||
OutlinedTextField(
|
||||
password, { password = it }, Modifier.fillMaxWidth(),
|
||||
label = { Text(stringResource(R.string.password)) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() }
|
||||
)
|
||||
if(VERSION.SDK_INT >= 29) {
|
||||
Row(Modifier.fillMaxWidth().padding(vertical = 4.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Column {
|
||||
Text(stringResource(R.string.proxy), Modifier.padding(end = 8.dp))
|
||||
Text(
|
||||
if(proxyAddress.isEmpty()) stringResource(R.string.none) else "$proxyAddress:$proxyPort",
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant, style = typography.bodyMedium
|
||||
)
|
||||
}
|
||||
TextButton({ dialog = 1 }) { Text(stringResource(R.string.edit)) }
|
||||
}
|
||||
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
||||
Column {
|
||||
Text(stringResource(R.string.mms_proxy), Modifier.padding(end = 8.dp))
|
||||
Text(
|
||||
if(mmsProxyAddress.isEmpty()) stringResource(R.string.none) else "$mmsProxyAddress:$mmsProxyPort",
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant, style = typography.bodyMedium
|
||||
)
|
||||
}
|
||||
TextButton({ dialog = 2 }) { Text(stringResource(R.string.edit)) }
|
||||
}
|
||||
}
|
||||
OutlinedTextField(
|
||||
mmsc, { mmsc = it }, Modifier.fillMaxWidth(),
|
||||
label = { Text("MMSC") },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() }
|
||||
)
|
||||
if(VERSION.SDK_INT >= 33) Row(Modifier.fillMaxWidth().padding(vertical = 4.dp), Arrangement.SpaceBetween) {
|
||||
val fr = FocusRequester()
|
||||
OutlinedTextField(
|
||||
mtuV4, { mtuV4 = it }, Modifier.fillMaxWidth(0.49F),
|
||||
label = { Text("MTU (IPv4)") },
|
||||
isError = !mtuV4.isEmpty() && 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,
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() }
|
||||
)
|
||||
}
|
||||
Row(Modifier.fillMaxWidth().padding(vertical = 10.dp), Arrangement.SpaceBetween, Alignment.CenterVertically) {
|
||||
val rotate by animateFloatAsState(if(dropdown == 2) 180F else 0F)
|
||||
val mvnoTypeMap = mapOf(
|
||||
ApnSetting.MVNO_TYPE_SPN to "SPM", ApnSetting.MVNO_TYPE_IMSI to "IMSI",
|
||||
ApnSetting.MVNO_TYPE_GID to "GID", ApnSetting.MVNO_TYPE_ICCID to "ICCID"
|
||||
)
|
||||
Text(stringResource(R.string.mvno_type))
|
||||
Row(Modifier.clickable { dropdown = 2 }.padding(4.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(mvnoTypeMap[mvnoType]!!, Modifier.padding(4.dp))
|
||||
Icon(Icons.Default.ArrowDropDown, null, Modifier.padding(start = 4.dp).rotate(rotate))
|
||||
DropdownMenu(dropdown == 2, { dropdown = 0 }) {
|
||||
mvnoTypeMap.forEach {
|
||||
DropdownMenuItem({ Text(it.value) }, { mvnoType = it.key; dropdown = 0 })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
OutlinedTextField(
|
||||
networkTypeBitmask, { networkTypeBitmask = it }, Modifier.fillMaxWidth(),
|
||||
label = { Text(stringResource(R.string.network_type_bitmask)) },
|
||||
isError = networkTypeBitmask.isNotEmpty() && networkTypeBitmask.toIntOrNull() == null,
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() }
|
||||
)
|
||||
OutlinedTextField(
|
||||
operatorNumeric, { operatorNumeric = it }, Modifier.fillMaxWidth().padding(vertical = 4.dp),
|
||||
label = { Text("Numeric operator ID") },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() }
|
||||
)
|
||||
Row(Modifier.fillMaxWidth().padding(vertical = 10.dp), Arrangement.SpaceBetween, Alignment.CenterVertically) {
|
||||
val rotate by animateFloatAsState(if(dropdown == 3) 180F else 0F)
|
||||
Text(stringResource(R.string.protocol))
|
||||
Row(Modifier.clickable { dropdown = 3 }.padding(4.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(protocolMap[protocol]!!, Modifier.padding(2.dp))
|
||||
Icon(Icons.Default.ArrowDropDown, null, Modifier.padding(start = 4.dp).rotate(rotate))
|
||||
DropdownMenu(dropdown == 3, { dropdown = 0 }) {
|
||||
protocolMap.forEach {
|
||||
DropdownMenuItem({ Text(it.value) }, { protocol = it.key; dropdown = 0 })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Row(Modifier.fillMaxWidth().padding(vertical = 10.dp), Arrangement.SpaceBetween, Alignment.CenterVertically) {
|
||||
val rotate by animateFloatAsState(if(dropdown == 4) 180F else 0F)
|
||||
Text(stringResource(R.string.roaming_protocol))
|
||||
Row(Modifier.clickable { dropdown = 4 }.padding(4.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(protocolMap[roamingProtocol]!!, Modifier.padding(2.dp))
|
||||
Icon(Icons.Default.ArrowDropDown, null, Modifier.padding(start = 4.dp).rotate(rotate))
|
||||
DropdownMenu(dropdown == 4, { dropdown = 0 }) {
|
||||
protocolMap.forEach {
|
||||
DropdownMenuItem({ Text(it.value) }, { roamingProtocol = it.key; dropdown = 0 })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(VERSION.SDK_INT >= 33) Row(Modifier.fillMaxWidth(), Arrangement.SpaceBetween, Alignment.CenterVertically) {
|
||||
Text(stringResource(R.string.persistent))
|
||||
Switch(persistent, { persistent = it })
|
||||
}
|
||||
Row(Modifier.fillMaxWidth(), Arrangement.SpaceBetween, Alignment.CenterVertically) {
|
||||
Text(stringResource(R.string.always_on))
|
||||
Switch(alwaysOn, { alwaysOn = it })
|
||||
}
|
||||
Button(
|
||||
{
|
||||
try {
|
||||
val setting = ApnSetting.Builder().apply {
|
||||
setCarrierEnabled(enabled)
|
||||
setApnName(apnName)
|
||||
setEntryName(entryName)
|
||||
setApnTypeBitmask(apnType)
|
||||
setAuthType(authType)
|
||||
setUser(user)
|
||||
setPassword(password)
|
||||
if(VERSION.SDK_INT >= 33) profileId.toIntOrNull()?.let { setProfileId(it) }
|
||||
if(VERSION.SDK_INT >= 29) {
|
||||
carrierId.toIntOrNull()?.let { setCarrierId(it) }
|
||||
setProxyAddress(proxyAddress)
|
||||
proxyPort.toIntOrNull()?.let { setProxyPort(it) }
|
||||
setMmsProxyAddress(mmsProxyAddress)
|
||||
mmsProxyPort.toIntOrNull()?.let { setMmsProxyPort(it) }
|
||||
}
|
||||
setMmsc(Uri.parse(mmsc))
|
||||
if(VERSION.SDK_INT >= 33) {
|
||||
mtuV4.toIntOrNull()?.let { setMtuV4(it) }
|
||||
mtuV6.toIntOrNull()?.let { setMtuV6(it) }
|
||||
}
|
||||
setMvnoType(mvnoType)
|
||||
networkTypeBitmask.toIntOrNull()?.let { setNetworkTypeBitmask(it) }
|
||||
setOperatorNumeric(operatorNumeric)
|
||||
setProtocol(protocol)
|
||||
setRoamingProtocol(roamingProtocol)
|
||||
if(VERSION.SDK_INT >= 33) setPersistent(persistent)
|
||||
if(VERSION.SDK_INT >= 35) setAlwaysOn(alwaysOn)
|
||||
}.build()
|
||||
if(origin == null) {
|
||||
dpm.addOverrideApn(receiver, setting)
|
||||
} else {
|
||||
dpm.updateOverrideApn(receiver, origin.id, setting)
|
||||
}
|
||||
onNavigateUp()
|
||||
} catch(e: Exception) {
|
||||
errorMessage = (e::class.qualifiedName ?: "") + "\n" + (e.message ?: "")
|
||||
}
|
||||
},
|
||||
Modifier.fillMaxWidth().padding(vertical = 4.dp)
|
||||
) {
|
||||
Text(stringResource(if(origin != null) R.string.update else R.string.add))
|
||||
}
|
||||
if(origin != null) Button(
|
||||
{
|
||||
dpm.removeOverrideApn(receiver, origin.id)
|
||||
onNavigateUp()
|
||||
},
|
||||
Modifier.fillMaxWidth(),
|
||||
colors = ButtonDefaults.buttonColors(MaterialTheme.colorScheme.error, MaterialTheme.colorScheme.onError)
|
||||
) {
|
||||
Text(stringResource(R.string.delete))
|
||||
}
|
||||
if(dialog != 0) {
|
||||
var address by remember { mutableStateOf((if(dialog == 1) proxyAddress else mmsProxyAddress)) }
|
||||
var port by remember { mutableStateOf((if(dialog == 1) proxyPort else mmsProxyPort)) }
|
||||
val fr = FocusRequester()
|
||||
AlertDialog(
|
||||
title = { Text(if(dialog == 1) "Proxy" else "MMS proxy") },
|
||||
text = {
|
||||
val fm = LocalFocusManager.current
|
||||
Column {
|
||||
OutlinedTextField(
|
||||
address, { address = it }, Modifier.fillMaxWidth().padding(bottom = 4.dp),
|
||||
textStyle = typography.bodyLarge,
|
||||
label = { Text(stringResource(R.string.address)) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
|
||||
keyboardActions = KeyboardActions { fr.requestFocus() }
|
||||
)
|
||||
OutlinedTextField(
|
||||
port, { port = it }, Modifier.fillMaxWidth().focusRequester(fr),
|
||||
textStyle = typography.bodyLarge,
|
||||
isError = port.isNotEmpty() && port.toIntOrNull() == null,
|
||||
label = { Text(stringResource(R.string.port)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() }
|
||||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
{
|
||||
if(dialog == 1) {
|
||||
proxyAddress = address
|
||||
proxyPort = port
|
||||
} else {
|
||||
mmsProxyAddress = address
|
||||
mmsProxyPort = port
|
||||
}
|
||||
dialog = 0
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton({ dialog = 0 }) { Text(stringResource(R.string.cancel)) }
|
||||
},
|
||||
onDismissRequest = { dialog = 0 }
|
||||
)
|
||||
}
|
||||
if(errorMessage != null) AlertDialog(
|
||||
title = { Text(stringResource(R.string.error)) },
|
||||
text = { Text(errorMessage ?: "") },
|
||||
confirmButton = {
|
||||
TextButton({ errorMessage = null }) { Text(stringResource(R.string.confirm)) }
|
||||
},
|
||||
onDismissRequest = { errorMessage = null }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
<string name="apply">Применить</string>
|
||||
<string name="decide_by_user">Определять пользователем</string>
|
||||
<string name="unsupported">Не поддерживается</string>
|
||||
<string name="developing">Разрабатываемая функция</string>
|
||||
<string name="options">Опции</string>
|
||||
<string name="copy_command">Копировать команду</string>
|
||||
<string name="package_name">Имя пакета</string>
|
||||
@@ -318,31 +317,30 @@
|
||||
<string name="included_uids">Включенные UID</string>
|
||||
<string name="excluded_uids">Исключенные UID</string>
|
||||
<string name="one_uid_per_line">Один UID на строку</string>
|
||||
<string name="override_apn_settings">Настройки APN</string>
|
||||
<string name="total_apn_amount">Количество настроек APN: %1$s</string>
|
||||
<string name="select_a_apn_or_create">Выберите настройку APN для редактирования (1~%1$s) или введите 0, чтобы создать новую настройку APN.</string>
|
||||
<string name="no_apn_you_should_create_one">Нет настроек APN. Будет создана новая.</string>
|
||||
<string name="previous_step">Предыдущий шаг</string>
|
||||
<string name="next_step">Следующий шаг</string>
|
||||
<!--TODO: following 4 strings-->
|
||||
<string name="override_apn">Override APN</string>
|
||||
<string name="apn_setting">APN setting</string>
|
||||
<string name="apn_name">APN name</string>
|
||||
<string name="entry_name">Entry name</string>
|
||||
<string name="name">Имя</string>
|
||||
<string name="user_name">Имя пользователя</string>
|
||||
<string name="profile_id">Идентификатор профиля</string>
|
||||
<string name="auth_type">Тип аутентификации</string>
|
||||
<string name="apn_type">Тип APN</string>
|
||||
<string name="bitmask">Битовая маска</string>
|
||||
<string name="description">Описание</string>
|
||||
<string name="mms_proxy">MMS-прокси</string>
|
||||
<string name="address">Адрес</string>
|
||||
<string name="port">Порт</string>
|
||||
<string name="proxy">Прокси</string>
|
||||
<string name="apn_network_type">Тип сети</string>
|
||||
<string name="persistent">Постоянный</string>
|
||||
<string name="protocol">Протокол</string>
|
||||
<string name="roaming_protocol">Протокол роуминга</string>
|
||||
<!--TODO: following 4 strings-->
|
||||
<string name="carrier_id">Carrier ID</string>
|
||||
<string name="mvno_type">MVNO type</string>
|
||||
<string name="network_type_bitmask">Network type bitmask</string>
|
||||
<string name="always_on">Always on</string>
|
||||
<string name="update">Обновить</string>
|
||||
|
||||
|
||||
|
||||
<!--Рабочий профиль-->
|
||||
<string name="work_profile">Рабочий профиль</string>
|
||||
<string name="work_profile_owner">Владелец профиля (рабочий профиль)</string>
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
<string name="apply">Uygula</string>
|
||||
<string name="decide_by_user">Kullanıcı Tarafından Karar Ver</string>
|
||||
<string name="unsupported">Desteklenmiyor</string>
|
||||
<string name="developing">Geliştirilen İşlev</string>
|
||||
<string name="options">Seçenekler</string>
|
||||
<string name="copy_command">Komutu Kopyala</string>
|
||||
<string name="package_name">Paket Adı</string>
|
||||
@@ -318,34 +317,34 @@
|
||||
<string name="export_logs">Export logs</string> <!--TODO-->
|
||||
<string name="wifi_auth_keypair">Wi-Fi anahtar çifti</string>
|
||||
<string name="preferential_network_service">Tercihli ağ hizmeti</string>
|
||||
<string name="add_config">Add config</string> <!--TODO-->
|
||||
<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="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>
|
||||
<string name="no_apn_you_should_create_one">APN ayarı yok. Yeni bir tane oluşturulacak.</string>
|
||||
<string name="previous_step">Önceki adım</string>
|
||||
<string name="next_step">Sonraki adım</string>
|
||||
<!--TODO: following 11 strings-->
|
||||
<string name="add_config">Add config</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="override_apn">Override APN</string>
|
||||
<string name="apn_setting">APN setting</string>
|
||||
<string name="apn_name">APN name</string>
|
||||
<string name="entry_name">Entry name</string>
|
||||
<string name="name">İsim</string>
|
||||
<string name="user_name">Kullanıcı adı</string>
|
||||
<string name="profile_id">Profil Kimliği</string>
|
||||
<string name="auth_type">Kimlik doğrulama türü</string>
|
||||
<string name="apn_type">APN türü</string>
|
||||
<string name="bitmask">Bitmask</string>
|
||||
<string name="description">Açıklama</string>
|
||||
<string name="mms_proxy">MMS proxy</string>
|
||||
<string name="address">Adres</string>
|
||||
<string name="port">Port</string>
|
||||
<string name="proxy">Proxy</string>
|
||||
<string name="apn_network_type">Ağ türü</string>
|
||||
<string name="persistent">Kalıcı</string>
|
||||
<string name="protocol">Protokol</string>
|
||||
<string name="roaming_protocol">Dolaşım protokolü</string>
|
||||
<!--TODO: following 4 strings-->
|
||||
<string name="carrier_id">Carrier ID</string>
|
||||
<string name="mvno_type">MVNO type</string>
|
||||
<string name="network_type_bitmask">Network type bitmask</string>
|
||||
<string name="always_on">Always on</string>
|
||||
<string name="update">Güncelle</string>
|
||||
|
||||
<!--WorkProfile-->
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
<string name="apply">应用</string>
|
||||
<string name="decide_by_user">由用户决定</string>
|
||||
<string name="unsupported">不支持</string>
|
||||
<string name="developing">功能开发中</string>
|
||||
<string name="options">选项</string>
|
||||
<string name="copy_command">复制代码</string>
|
||||
<string name="copy">复制</string>
|
||||
@@ -312,28 +311,26 @@
|
||||
<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="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>
|
||||
<string name="no_apn_you_should_create_one">当前没有APN设置,你可以新建一个APN设置</string>
|
||||
<string name="previous_step">上一步</string>
|
||||
<string name="next_step">下一步</string>
|
||||
<string name="one_uid_per_line">每行一个UID</string>
|
||||
<string name="override_apn">覆盖APN</string>
|
||||
<string name="apn_setting">APN设置</string>
|
||||
<string name="apn_name">APN名称</string>
|
||||
<string name="entry_name">条目名称</string>
|
||||
<string name="name">名称</string>
|
||||
<string name="user_name">用户名</string>
|
||||
<string name="profile_id">资料ID</string>
|
||||
<string name="auth_type">验证类型</string>
|
||||
<string name="apn_type">APN类型</string>
|
||||
<string name="bitmask">位掩码</string>
|
||||
<string name="auth_type">认证类型</string>
|
||||
<string name="description">描述</string>
|
||||
<string name="mms_proxy">MMS代理</string>
|
||||
<string name="address">地址</string>
|
||||
<string name="port">端口</string>
|
||||
<string name="proxy">代理</string>
|
||||
<string name="apn_network_type">网络类型</string>
|
||||
<string name="persistent">持久的</string>
|
||||
<string name="persistent">持久化</string>
|
||||
<string name="protocol">协议</string>
|
||||
<string name="roaming_protocol">漫游协议</string>
|
||||
<string name="carrier_id">运营商ID</string>
|
||||
<string name="mvno_type">MVNO类型</string>
|
||||
<string name="network_type_bitmask">网络类型位掩码</string>
|
||||
<string name="always_on">总是开启</string>
|
||||
<string name="update">更新</string>
|
||||
|
||||
<!--WorkProfile-->
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
<string name="apply">Apply</string>
|
||||
<string name="decide_by_user">Decide by user</string>
|
||||
<string name="unsupported">Unsupported</string>
|
||||
<string name="developing">Developing function</string>
|
||||
<string name="options">Options</string>
|
||||
<string name="copy_command">Copy Command</string>
|
||||
<string name="package_name">Package name</string>
|
||||
@@ -346,27 +345,25 @@
|
||||
<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="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>
|
||||
<string name="no_apn_you_should_create_one">No APN settings. Will create a new one. </string>
|
||||
<string name="previous_step">Previous step</string>
|
||||
<string name="next_step">Next step</string>
|
||||
<string name="override_apn">Override APN</string>
|
||||
<string name="apn_setting">APN setting</string>
|
||||
<string name="apn_name">APN name</string>
|
||||
<string name="entry_name">Entry name</string>
|
||||
<string name="name">Name</string>
|
||||
<string name="user_name">User name</string>
|
||||
<string name="profile_id">Profile ID</string>
|
||||
<string name="auth_type">Auth type</string>
|
||||
<string name="apn_type">APN type</string>
|
||||
<string name="bitmask">Bitmask</string>
|
||||
<string name="description">Description</string>
|
||||
<string name="mms_proxy">MMS proxy</string>
|
||||
<string name="address">Address</string>
|
||||
<string name="port">Port</string>
|
||||
<string name="proxy">Proxy</string>
|
||||
<string name="apn_network_type">Network type</string>
|
||||
<string name="persistent">Persistent</string>
|
||||
<string name="protocol">Protocol</string>
|
||||
<string name="roaming_protocol">Roaming protocol</string>
|
||||
<string name="carrier_id">Carrier ID</string>
|
||||
<string name="mvno_type">MVNO type</string>
|
||||
<string name="network_type_bitmask">Network type bitmask</string>
|
||||
<string name="always_on">Always on</string>
|
||||
<string name="update">Update</string>
|
||||
|
||||
<!--WorkProfile-->
|
||||
|
||||
Reference in New Issue
Block a user