Merge "Duplicate apn entry" into main
This commit is contained in:
@@ -3323,6 +3323,8 @@
|
||||
<string name="menu_cancel">Cancel</string>
|
||||
<!-- APN error dialog title -->
|
||||
<string name="error_title"></string>
|
||||
<!-- APN error dialog messages when the new apn is a duplicate: -->
|
||||
<string name="error_duplicate_apn_entry">Duplicate apn entry.</string>
|
||||
<!-- APN error dialog messages: -->
|
||||
<string name="error_name_empty">The Name field can\u2019t be empty.</string>
|
||||
<!-- APN error dialog messages: -->
|
||||
|
@@ -19,16 +19,21 @@ package com.android.settings.network.apn
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Done
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringArrayResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -39,6 +44,7 @@ import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeDisplayNam
|
||||
import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeSelectedOptionsState
|
||||
import com.android.settingslib.spa.framework.common.SettingsPageProvider
|
||||
import com.android.settingslib.spa.framework.compose.LocalNavController
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuBox
|
||||
import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuCheckBox
|
||||
import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
|
||||
@@ -98,25 +104,47 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState<ApnData>, uriInit: Ur
|
||||
getNetworkTypeSelectedOptionsState(apnData.networkType)
|
||||
}
|
||||
val navController = LocalNavController.current
|
||||
var valid: String?
|
||||
RegularScaffold(
|
||||
title = if (apnDataInit.newApn) stringResource(id = R.string.apn_add) else stringResource(id = R.string.apn_edit),
|
||||
actions = {
|
||||
if (!apnData.customizedConfig.readOnlyApn) {
|
||||
IconButton(onClick = {
|
||||
if (!apnData.validEnabled) apnData = apnData.copy(validEnabled = true)
|
||||
val valid = validateAndSaveApnData(
|
||||
apnData = apnData.copy(
|
||||
networkType = ApnNetworkTypes.getNetworkType(
|
||||
networkTypeSelectedOptionsState
|
||||
)
|
||||
)
|
||||
valid = validateAndSaveApnData(
|
||||
apnDataInit,
|
||||
apnData,
|
||||
context,
|
||||
uriInit,
|
||||
networkTypeSelectedOptionsState
|
||||
uriInit
|
||||
)
|
||||
if (valid) navController.navigateBack()
|
||||
if (valid == null) navController.navigateBack()
|
||||
else if (!apnData.validEnabled) apnData = apnData.copy(validEnabled = true)
|
||||
}) { Icon(imageVector = Icons.Outlined.Done, contentDescription = null) }
|
||||
}
|
||||
},
|
||||
) {
|
||||
Column {
|
||||
if (apnData.validEnabled) {
|
||||
apnData = apnData.copy(
|
||||
networkType = ApnNetworkTypes.getNetworkType(
|
||||
networkTypeSelectedOptionsState
|
||||
)
|
||||
)
|
||||
valid = validateApnData(uriInit, apnData, context)
|
||||
valid?.let {
|
||||
Text(
|
||||
text = it,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(SettingsDimension.menuFieldPadding),
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
SettingsOutlinedTextField(
|
||||
value = apnData.name,
|
||||
label = stringResource(R.string.apn_name),
|
||||
|
@@ -20,6 +20,7 @@ import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.Telephony
|
||||
import android.telephony.TelephonyManager
|
||||
import android.util.Log
|
||||
import com.android.settings.R
|
||||
import com.android.settingslib.utils.ThreadUtils
|
||||
@@ -150,7 +151,6 @@ fun getApnDataFromUri(uri: Uri, context: Context): ApnData {
|
||||
private fun convertProtocol2Options(raw: String, context: Context): String {
|
||||
val apnProtocolOptions = context.resources.getStringArray(R.array.apn_protocol_entries).toList()
|
||||
val apnProtocolValues = context.resources.getStringArray(R.array.apn_protocol_values).toList()
|
||||
|
||||
var uRaw = raw.uppercase(Locale.getDefault())
|
||||
uRaw = if (uRaw == "IPV4") "IP" else uRaw
|
||||
val protocolIndex = apnProtocolValues.indexOf(uRaw)
|
||||
@@ -167,7 +167,6 @@ private fun convertProtocol2Options(raw: String, context: Context): String {
|
||||
|
||||
fun convertOptions2Protocol(protocolIndex: Int, context: Context): String {
|
||||
val apnProtocolValues = context.resources.getStringArray(R.array.apn_protocol_values).toList()
|
||||
|
||||
return if (protocolIndex == -1) {
|
||||
""
|
||||
} else {
|
||||
@@ -179,7 +178,12 @@ fun convertOptions2Protocol(protocolIndex: Int, context: Context): String {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateApnDataToDatabase(newApn: Boolean, values: ContentValues, context: Context, uriInit: Uri) {
|
||||
fun updateApnDataToDatabase(
|
||||
newApn: Boolean,
|
||||
values: ContentValues,
|
||||
context: Context,
|
||||
uriInit: Uri
|
||||
) {
|
||||
ThreadUtils.postOnBackgroundThread {
|
||||
if (newApn) {
|
||||
// Add a new apn to the database
|
||||
@@ -194,4 +198,24 @@ fun updateApnDataToDatabase(newApn: Boolean, values: ContentValues, context: Con
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isItemExist(uri: Uri, apnData: ApnData, context: Context): String? {
|
||||
val contentValueMap = apnData.getContentValueMap(context)
|
||||
contentValueMap.remove(Telephony.Carriers.CARRIER_ENABLED)
|
||||
val list = contentValueMap.entries.toList()
|
||||
val selection = list.joinToString(" AND ") { "${it.key} = ?" }
|
||||
val selectionArgs: Array<String> = list.map { it.value.toString() }.toTypedArray()
|
||||
context.contentResolver.query(
|
||||
uri,
|
||||
sProjection,
|
||||
selection /* selection */,
|
||||
selectionArgs /* selectionArgs */,
|
||||
null /* sortOrder */
|
||||
)?.use { cursor ->
|
||||
if (cursor.count > 0) {
|
||||
return context.resources.getString(R.string.error_duplicate_apn_entry)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
@@ -72,41 +72,38 @@ data class ApnData(
|
||||
val validEnabled: Boolean = false,
|
||||
val customizedConfig: CustomizedConfig = CustomizedConfig()
|
||||
) {
|
||||
fun getContentValueMap(context: Context): MutableMap<String, Any> {
|
||||
val simCarrierId =
|
||||
context.getSystemService(TelephonyManager::class.java)!!
|
||||
.createForSubscriptionId(subId)
|
||||
.getSimCarrierId()
|
||||
return mutableMapOf(
|
||||
Telephony.Carriers.NAME to name, Telephony.Carriers.APN to apn,
|
||||
Telephony.Carriers.PROXY to proxy, Telephony.Carriers.PORT to port,
|
||||
Telephony.Carriers.MMSPROXY to mmsProxy, Telephony.Carriers.MMSPORT to mmsPort,
|
||||
Telephony.Carriers.USER to userName, Telephony.Carriers.SERVER to server,
|
||||
Telephony.Carriers.PASSWORD to passWord, Telephony.Carriers.MMSC to mmsc,
|
||||
Telephony.Carriers.AUTH_TYPE to authType,
|
||||
Telephony.Carriers.PROTOCOL to convertOptions2Protocol(apnProtocol, context),
|
||||
Telephony.Carriers.ROAMING_PROTOCOL to convertOptions2Protocol(apnRoaming, context),
|
||||
Telephony.Carriers.TYPE to apnType,
|
||||
Telephony.Carriers.NETWORK_TYPE_BITMASK to networkType,
|
||||
Telephony.Carriers.CARRIER_ENABLED to apnEnable,
|
||||
Telephony.Carriers.EDITED_STATUS to Telephony.Carriers.USER_EDITED,
|
||||
Telephony.Carriers.CARRIER_ID to simCarrierId
|
||||
)
|
||||
}
|
||||
|
||||
fun getContentValues(context: Context): ContentValues {
|
||||
val values = ContentValues()
|
||||
values.put(Telephony.Carriers.NAME, name)
|
||||
values.put(Telephony.Carriers.APN, apn)
|
||||
values.put(Telephony.Carriers.PROXY, proxy)
|
||||
values.put(Telephony.Carriers.PORT, port)
|
||||
values.put(Telephony.Carriers.MMSPROXY, mmsProxy)
|
||||
values.put(Telephony.Carriers.MMSPORT, mmsPort)
|
||||
values.put(Telephony.Carriers.USER, userName)
|
||||
values.put(Telephony.Carriers.SERVER, server)
|
||||
values.put(Telephony.Carriers.PASSWORD, passWord)
|
||||
values.put(Telephony.Carriers.MMSC, mmsc)
|
||||
values.put(Telephony.Carriers.AUTH_TYPE, authType)
|
||||
values.put(Telephony.Carriers.PROTOCOL, convertOptions2Protocol(apnProtocol, context))
|
||||
values.put(
|
||||
Telephony.Carriers.ROAMING_PROTOCOL,
|
||||
convertOptions2Protocol(apnRoaming, context)
|
||||
)
|
||||
values.put(Telephony.Carriers.TYPE, apnType)
|
||||
values.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, networkType)
|
||||
values.put(Telephony.Carriers.CARRIER_ENABLED, apnEnable)
|
||||
values.put(Telephony.Carriers.EDITED_STATUS, Telephony.Carriers.USER_EDITED)
|
||||
if (newApn) {
|
||||
val simCarrierId =
|
||||
context.getSystemService(TelephonyManager::class.java)!!
|
||||
.createForSubscriptionId(subId)
|
||||
.getSimCarrierId()
|
||||
values.put(Telephony.Carriers.CARRIER_ID, simCarrierId)
|
||||
}
|
||||
val contentValueMap = getContentValueMap(context)
|
||||
if (!newApn) contentValueMap.remove(Telephony.Carriers.CARRIER_ID)
|
||||
contentValueMap.forEach { (key, value) -> values.putObject(key, value) }
|
||||
return values
|
||||
}
|
||||
}
|
||||
|
||||
data class CustomizedConfig(
|
||||
val newApn: Boolean = false,
|
||||
val readOnlyApn: Boolean = false,
|
||||
val isAddApnAllowed: Boolean = true,
|
||||
val readOnlyApnTypes: List<String> = emptyList(),
|
||||
@@ -227,20 +224,14 @@ fun getApnDataInit(arguments: Bundle, context: Context, uriInit: Uri, subId: Int
|
||||
*/
|
||||
fun validateAndSaveApnData(
|
||||
apnDataInit: ApnData,
|
||||
apnData: ApnData,
|
||||
newApnData: ApnData,
|
||||
context: Context,
|
||||
uriInit: Uri,
|
||||
networkTypeSelectedOptionsState: SnapshotStateList<Int>
|
||||
): Boolean {
|
||||
// Nothing to do if it's a read only APN
|
||||
if (apnData.customizedConfig.readOnlyApn) {
|
||||
return true
|
||||
}
|
||||
val errorMsg = validateApnData(apnData, context)
|
||||
uriInit: Uri
|
||||
): String? {
|
||||
val errorMsg = validateApnData(uriInit, newApnData, context)
|
||||
if (errorMsg != null) {
|
||||
return false
|
||||
return errorMsg
|
||||
}
|
||||
val newApnData = apnData.copy(networkType = getNetworkType(networkTypeSelectedOptionsState))
|
||||
if (newApnData.newApn || (newApnData != apnDataInit)) {
|
||||
Log.d(TAG, "[validateAndSaveApnData] newApnData.networkType: ${newApnData.networkType}")
|
||||
updateApnDataToDatabase(
|
||||
@@ -250,7 +241,7 @@ fun validateAndSaveApnData(
|
||||
uriInit
|
||||
)
|
||||
}
|
||||
return true
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,7 +249,7 @@ fun validateAndSaveApnData(
|
||||
*
|
||||
* @return An error message if the apn data is invalid, otherwise return null.
|
||||
*/
|
||||
fun validateApnData(apnData: ApnData, context: Context): String? {
|
||||
fun validateApnData(uri: Uri, apnData: ApnData, context: Context): String? {
|
||||
var errorMsg: String?
|
||||
val name = apnData.name
|
||||
val apn = apnData.apn
|
||||
@@ -267,11 +258,14 @@ fun validateApnData(apnData: ApnData, context: Context): String? {
|
||||
} else if (apn == "") {
|
||||
context.resources.getString(R.string.error_apn_empty)
|
||||
} else {
|
||||
validateMMSC(apnData.validEnabled, apnData.mmsc, context)
|
||||
validateMMSC(true, apnData.mmsc, context)
|
||||
}
|
||||
if (errorMsg == null) {
|
||||
errorMsg = isItemExist(uri, apnData, context)
|
||||
}
|
||||
if (errorMsg == null) {
|
||||
errorMsg = validateAPNType(
|
||||
apnData.validEnabled,
|
||||
true,
|
||||
apnData.apnType,
|
||||
apnData.customizedConfig.readOnlyApnTypes,
|
||||
context
|
||||
|
Reference in New Issue
Block a user