Add validateAndSaveApnData.

Fix: 304649927
Test: Visual Test
Change-Id: I900a096a6e27f1db66af204201d4ca2523537c0d
This commit is contained in:
Charlotte Lu
2023-10-11 10:55:20 +08:00
parent 74927c7e11
commit 5b2de59480
4 changed files with 189 additions and 33 deletions

View File

@@ -19,6 +19,10 @@ package com.android.settings.network.apn
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
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.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -70,7 +74,7 @@ object ApnEditPageProvider : SettingsPageProvider {
val apnDataCur = remember { val apnDataCur = remember {
mutableStateOf(apnDataInit) mutableStateOf(apnDataInit)
} }
ApnPage(apnDataCur) ApnPage(apnDataInit, apnDataCur)
} }
fun getRoute( fun getRoute(
@@ -83,7 +87,7 @@ object ApnEditPageProvider : SettingsPageProvider {
} }
@Composable @Composable
fun ApnPage(apnDataCur: MutableState<ApnData>) { fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState<ApnData>) {
var apnData by apnDataCur var apnData by apnDataCur
val context = LocalContext.current val context = LocalContext.current
val authTypeOptions = stringArrayResource(R.array.apn_auth_entries).toList() val authTypeOptions = stringArrayResource(R.array.apn_auth_entries).toList()
@@ -93,6 +97,11 @@ fun ApnPage(apnDataCur: MutableState<ApnData>) {
} }
RegularScaffold( RegularScaffold(
title = stringResource(id = R.string.apn_edit), title = stringResource(id = R.string.apn_edit),
actions = {
IconButton(onClick = {
validateAndSaveApnData(apnDataInit, apnData, context)
}) { Icon(imageVector = Icons.Outlined.Done, contentDescription = "Save APN") }
}
) { ) {
Column() { Column() {
SettingsOutlinedTextField( SettingsOutlinedTextField(

View File

@@ -81,6 +81,55 @@ data class CustomizedConfig(
val defaultApnRoamingProtocol: String = "", val defaultApnRoamingProtocol: String = "",
) )
/**
* APN types for data connections. These are usage categories for an APN
* entry. One APN entry may support multiple APN types, eg, a single APN
* may service regular internet traffic ("default") as well as MMS-specific
* connections.<br></br>
* APN_TYPE_ALL is a special type to indicate that this APN entry can
* service all data connections.
*/
const val APN_TYPE_ALL = "*"
/** APN type for default data traffic */
const val APN_TYPE_DEFAULT = "default"
/** APN type for MMS traffic */
const val APN_TYPE_MMS = "mms"
/** APN type for SUPL assisted GPS */
const val APN_TYPE_SUPL = "supl"
/** APN type for DUN traffic */
const val APN_TYPE_DUN = "dun"
/** APN type for HiPri traffic */
const val APN_TYPE_HIPRI = "hipri"
/** APN type for FOTA */
const val APN_TYPE_FOTA = "fota"
/** APN type for IMS */
const val APN_TYPE_IMS = "ims"
/** APN type for CBS */
const val APN_TYPE_CBS = "cbs"
/** APN type for IA Initial Attach APN */
const val APN_TYPE_IA = "ia"
/** APN type for Emergency PDN. This is not an IA apn, but is used
* for access to carrier services in an emergency call situation. */
const val APN_TYPE_EMERGENCY = "emergency"
/** APN type for Mission Critical Services */
const val APN_TYPE_MCX = "mcx"
/** APN type for XCAP */
const val APN_TYPE_XCAP = "xcap"
val APN_TYPES = arrayOf(
APN_TYPE_DEFAULT,
APN_TYPE_MMS,
APN_TYPE_SUPL,
APN_TYPE_DUN,
APN_TYPE_HIPRI,
APN_TYPE_FOTA,
APN_TYPE_IMS,
APN_TYPE_CBS,
APN_TYPE_IA,
APN_TYPE_EMERGENCY,
APN_TYPE_MCX,
APN_TYPE_XCAP
)
/** /**
* Initialize ApnData according to the arguments. * Initialize ApnData according to the arguments.
* @param arguments The data passed in when the user calls PageProvider. * @param arguments The data passed in when the user calls PageProvider.
@@ -123,6 +172,108 @@ fun getApnDataInit(arguments: Bundle, context: Context, uriInit: Uri, subId: Int
return apnDataInit return apnDataInit
} }
/**
* Validates the apn data and save it to the database if it's valid.
*
*
*
* A dialog with error message will be displayed if the APN data is invalid.
*
* @return true if there is no error
*/
fun validateAndSaveApnData(apnDataInit: ApnData, apnData: ApnData, context: Context): Boolean {
// Nothing to do if it's a read only APN
if (apnData.customizedConfig.readOnlyApn) {
return true
}
val errorMsg = validateApnData(apnData, context)
if (errorMsg != null) {
//TODO: showError(this)
return false
}
if (apnData.newApn || (apnData != apnDataInit)) {
Log.d(TAG, "validateAndSaveApnData: apnData ${apnData.name}")
// TODO: updateApnDataToDatabase
}
return true
}
/**
* Validates whether the apn data is valid.
*
* @return An error message if the apn data is invalid, otherwise return null.
*/
fun validateApnData(apnData: ApnData, context: Context): String? {
var errorMsg: String? = null
val name = apnData.name
val apn = apnData.apn
if (name == "") {
errorMsg = context.resources.getString(R.string.error_name_empty)
} else if (apn == "") {
errorMsg = context.resources.getString(R.string.error_apn_empty)
}
if (errorMsg == null) {
// if carrier does not allow editing certain apn types, make sure type does not include
// those
if (!ArrayUtils.isEmpty(apnData.customizedConfig.readOnlyApnTypes)
&& apnTypesMatch(apnData.customizedConfig.readOnlyApnTypes, getUserEnteredApnType(apnData.apnType, apnData.customizedConfig.readOnlyApnTypes))
) {
val stringBuilder = StringBuilder()
for (type in apnData.customizedConfig.readOnlyApnTypes) {
stringBuilder.append(type).append(", ")
Log.d(TAG, "validateApnData: appending type: $type")
}
// remove last ", "
if (stringBuilder.length >= 2) {
stringBuilder.delete(stringBuilder.length - 2, stringBuilder.length)
}
errorMsg = String.format(
context.resources.getString(R.string.error_adding_apn_type),
stringBuilder
)
}
}
return errorMsg
}
private fun getUserEnteredApnType(apnType: String, readOnlyApnTypes: List<String>): String {
// if user has not specified a type, map it to "ALL APN TYPES THAT ARE NOT READ-ONLY"
// but if user enter empty type, map it just for default
var userEnteredApnType = apnType
if (userEnteredApnType != "") userEnteredApnType =
userEnteredApnType.trim { it <= ' ' }
if (TextUtils.isEmpty(userEnteredApnType) || APN_TYPE_ALL == userEnteredApnType) {
userEnteredApnType = getEditableApnType(readOnlyApnTypes)
}
Log.d(
TAG, "getUserEnteredApnType: changed apn type to editable apn types: "
+ userEnteredApnType
)
return userEnteredApnType
}
private fun getEditableApnType(readOnlyApnTypes: List<String>): String {
val editableApnTypes = StringBuilder()
var first = true
for (apnType in APN_TYPES) {
// add APN type if it is not read-only and is not wild-cardable
if (!readOnlyApnTypes.contains(apnType)
&& apnType != APN_TYPE_IA
&& apnType != APN_TYPE_EMERGENCY
&& apnType != APN_TYPE_MCX
&& apnType != APN_TYPE_IMS
) {
if (first) {
first = false
} else {
editableApnTypes.append(",")
}
editableApnTypes.append(apnType)
}
}
return editableApnTypes.toString()
}
/** /**
* Initialize CustomizedConfig information through subId. * Initialize CustomizedConfig information through subId.
* @param subId subId information obtained from arguments. * @param subId subId information obtained from arguments.
@@ -299,11 +450,11 @@ fun hasAllApns(apnTypes: Array<String?>): Boolean {
return false return false
} }
val apnList: List<*> = Arrays.asList(*apnTypes) val apnList: List<*> = Arrays.asList(*apnTypes)
if (apnList.contains(ApnEditor.APN_TYPE_ALL)) { if (apnList.contains(APN_TYPE_ALL)) {
Log.d(TAG, "hasAllApns: true because apnList.contains(APN_TYPE_ALL)") Log.d(TAG, "hasAllApns: true because apnList.contains(APN_TYPE_ALL)")
return true return true
} }
for (apn in ApnEditor.APN_TYPES) { for (apn in APN_TYPES) {
if (!apnList.contains(apn)) { if (!apnList.contains(apn)) {
return false return false
} }

View File

@@ -17,7 +17,6 @@
package com.android.settings.network.apn package com.android.settings.network.apn
import android.content.Context import android.content.Context
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsDisplayed
@@ -58,8 +57,7 @@ class ApnEditPageProviderTest {
context.resources.getStringArray(R.array.apn_protocol_entries).toList() context.resources.getStringArray(R.array.apn_protocol_entries).toList()
private val networkType = context.resources.getString(R.string.network_type) private val networkType = context.resources.getString(R.string.network_type)
private val passwordTitle = context.resources.getString(R.string.apn_password) private val passwordTitle = context.resources.getString(R.string.apn_password)
private val apnData = mutableStateOf( private val apnInit = ApnData(
ApnData(
name = apnName, name = apnName,
mmsc = mmsc, mmsc = mmsc,
mmsProxy = mmsProxy, mmsProxy = mmsProxy,
@@ -67,6 +65,8 @@ class ApnEditPageProviderTest {
apnRoaming = apnProtocolOptions.indexOf(apnRoaming), apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
apnEnable = true apnEnable = true
) )
private val apnData = mutableStateOf(
apnInit
) )
@Test @Test
@@ -77,7 +77,7 @@ class ApnEditPageProviderTest {
@Test @Test
fun title_displayed() { fun title_displayed() {
composeTestRule.setContent { composeTestRule.setContent {
ApnPage(remember { ApnPage(apnInit, remember {
apnData apnData
}) })
} }
@@ -87,7 +87,7 @@ class ApnEditPageProviderTest {
@Test @Test
fun name_displayed() { fun name_displayed() {
composeTestRule.setContent { composeTestRule.setContent {
ApnPage(remember { ApnPage(apnInit, remember {
apnData apnData
}) })
} }
@@ -97,7 +97,7 @@ class ApnEditPageProviderTest {
@Test @Test
fun mmsc_displayed() { fun mmsc_displayed() {
composeTestRule.setContent { composeTestRule.setContent {
ApnPage(remember { ApnPage(apnInit, remember {
apnData apnData
}) })
} }
@@ -109,7 +109,7 @@ class ApnEditPageProviderTest {
@Test @Test
fun mms_proxy_displayed() { fun mms_proxy_displayed() {
composeTestRule.setContent { composeTestRule.setContent {
ApnPage(remember { ApnPage(apnInit, remember {
apnData apnData
}) })
} }
@@ -121,7 +121,7 @@ class ApnEditPageProviderTest {
@Test @Test
fun apn_type_displayed() { fun apn_type_displayed() {
composeTestRule.setContent { composeTestRule.setContent {
ApnPage(remember { ApnPage(apnInit, remember {
apnData apnData
}) })
} }
@@ -133,7 +133,7 @@ class ApnEditPageProviderTest {
@Test @Test
fun apn_roaming_displayed() { fun apn_roaming_displayed() {
composeTestRule.setContent { composeTestRule.setContent {
ApnPage(remember { ApnPage(apnInit, remember {
apnData apnData
}) })
} }
@@ -145,7 +145,7 @@ class ApnEditPageProviderTest {
@Test @Test
fun carrier_enabled_displayed() { fun carrier_enabled_displayed() {
composeTestRule.setContent { composeTestRule.setContent {
ApnPage(remember { ApnPage(apnInit, remember {
apnData apnData
}) })
} }
@@ -157,7 +157,7 @@ class ApnEditPageProviderTest {
@Test @Test
fun carrier_enabled_isChecked() { fun carrier_enabled_isChecked() {
composeTestRule.setContent { composeTestRule.setContent {
ApnPage(remember { ApnPage(apnInit, remember {
apnData apnData
}) })
} }
@@ -169,7 +169,7 @@ class ApnEditPageProviderTest {
@Test @Test
fun carrier_enabled_checkChanged() { fun carrier_enabled_checkChanged() {
composeTestRule.setContent { composeTestRule.setContent {
ApnPage(remember { ApnPage(apnInit, remember {
apnData apnData
}) })
} }
@@ -182,7 +182,7 @@ class ApnEditPageProviderTest {
@Test @Test
fun network_type_displayed() { fun network_type_displayed() {
composeTestRule.setContent { composeTestRule.setContent {
ApnPage(remember { ApnPage(apnInit, remember {
apnData apnData
}) })
} }
@@ -193,12 +193,10 @@ class ApnEditPageProviderTest {
@Test @Test
fun network_type_changed() { fun network_type_changed() {
var apnDataa: MutableState<ApnData> = apnData
composeTestRule.setContent { composeTestRule.setContent {
apnDataa = remember { ApnPage(apnInit, remember {
apnData apnData
} })
ApnPage(apnDataa)
} }
composeTestRule.onRoot().onChild().onChildAt(0) composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(networkType, true)) .performScrollToNode(hasText(networkType, true))
@@ -211,12 +209,10 @@ class ApnEditPageProviderTest {
@Test @Test
fun network_type_changed_back2Default() { fun network_type_changed_back2Default() {
var apnDataa: MutableState<ApnData> = apnData
composeTestRule.setContent { composeTestRule.setContent {
apnDataa = remember { ApnPage(apnInit, remember {
apnData apnData
} })
ApnPage(apnDataa)
} }
composeTestRule.onRoot().onChild().onChildAt(0) composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(networkType, true)) .performScrollToNode(hasText(networkType, true))
@@ -234,7 +230,7 @@ class ApnEditPageProviderTest {
@Test @Test
fun password_displayed() { fun password_displayed() {
composeTestRule.setContent { composeTestRule.setContent {
ApnPage(remember { ApnPage(apnInit, remember {
apnData apnData
}) })
} }