Merge "Duplicate apn entry" into main

This commit is contained in:
Charlotte Lu
2024-01-18 04:00:58 +00:00
committed by Android (Google) Code Review
4 changed files with 99 additions and 51 deletions

View File

@@ -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: -->

View File

@@ -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),

View File

@@ -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
}

View File

@@ -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