Add validateAndSaveApnData.
Fix: 304649927 Test: Visual Test Change-Id: I900a096a6e27f1db66af204201d4ca2523537c0d
This commit is contained in:
@@ -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(
|
||||||
|
@@ -161,4 +161,4 @@ private fun convertProtocol2Options(raw: String, context: Context): String {
|
|||||||
""
|
""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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,15 +57,16 @@ 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 apnInit = ApnData(
|
||||||
|
name = apnName,
|
||||||
|
mmsc = mmsc,
|
||||||
|
mmsProxy = mmsProxy,
|
||||||
|
apnType = apnType,
|
||||||
|
apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
|
||||||
|
apnEnable = true
|
||||||
|
)
|
||||||
private val apnData = mutableStateOf(
|
private val apnData = mutableStateOf(
|
||||||
ApnData(
|
apnInit
|
||||||
name = apnName,
|
|
||||||
mmsc = mmsc,
|
|
||||||
mmsProxy = mmsProxy,
|
|
||||||
apnType = apnType,
|
|
||||||
apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
|
|
||||||
apnEnable = true
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user