Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f577f7d authored by Charlotte Lu's avatar Charlotte Lu
Browse files

Change apn type into ExposedDropdownMenuCheckBox

Test: Visual Test
Fix: 320891903,320621937
Change-Id: Ia6e4dbbcb568afae04776658a025e30c69e1ddd8
parent e0f6540f
Loading
Loading
Loading
Loading
+52 −32
Original line number Diff line number Diff line
@@ -40,6 +40,10 @@ import androidx.navigation.navArgument
import com.android.settings.R
import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeDisplayNames
import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeSelectedOptionsState
import com.android.settings.network.apn.ApnTypes.APN_TYPES_OPTIONS
import com.android.settings.network.apn.ApnTypes.APN_TYPE_MMS
import com.android.settings.network.apn.ApnTypes.getApnTypeSelectedOptionsState
import com.android.settings.network.apn.ApnTypes.updateApnType
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.LocalNavController
import com.android.settingslib.spa.framework.theme.SettingsDimension
@@ -100,6 +104,9 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState<ApnData>, uriInit: Ur
    val networkTypeSelectedOptionsState = remember {
        getNetworkTypeSelectedOptionsState(apnData.networkType)
    }
    var apnTypeSelectedOptionsState = remember {
        getApnTypeSelectedOptionsState(apnData.apnType)
    }
    val navController = LocalNavController.current
    var valid: String?
    RegularScaffold(
@@ -191,6 +198,27 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState<ApnData>, uriInit: Ur
                label = stringResource(R.string.apn_server),
                enabled = apnData.serverEnabled
            ) { apnData = apnData.copy(server = it) }
            SettingsExposedDropdownMenuCheckBox(
                label = stringResource(R.string.apn_type),
                options = APN_TYPES_OPTIONS,
                selectedOptionsState = apnTypeSelectedOptionsState,
                enabled = apnData.apnTypeEnabled,
                errorMessage = validateAPNType(
                    apnData.validEnabled, apnData.apnType,
                    apnData.customizedConfig.readOnlyApnTypes, context
                )
            ) {
                val apnType = updateApnType(
                    apnTypeSelectedOptionsState,
                    apnData.customizedConfig.defaultApnTypes,
                    apnData.customizedConfig.readOnlyApnTypes
                )
                apnTypeSelectedOptionsState = getApnTypeSelectedOptionsState(apnType)
                apnData = apnData.copy(
                    apnType = apnType
                )
            }
            if (apnTypeSelectedOptionsState.contains(APN_TYPES_OPTIONS.indexOf(APN_TYPE_MMS))) {
                SettingsOutlinedTextField(
                    value = apnData.mmsc,
                    label = stringResource(R.string.apn_mmsc),
@@ -207,21 +235,13 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState<ApnData>, uriInit: Ur
                    label = stringResource(R.string.apn_mms_port),
                    enabled = apnData.mmsPortEnabled
                ) { apnData = apnData.copy(mmsPort = it) }
            }
            SettingsExposedDropdownMenuBox(
                label = stringResource(R.string.apn_auth_type),
                options = authTypeOptions,
                selectedOptionIndex = apnData.authType,
                enabled = apnData.authTypeEnabled,
            ) { apnData = apnData.copy(authType = it) }
            SettingsOutlinedTextField(
                value = apnData.apnType,
                label = stringResource(R.string.apn_type),
                enabled = apnData.apnTypeEnabled,
                errorMessage = validateAPNType(
                    apnData.validEnabled, apnData.apnType,
                    apnData.customizedConfig.readOnlyApnTypes, context
                )
            ) { apnData = apnData.copy(apnType = updateApnType(apnData.copy(apnType = it))) }
            SettingsExposedDropdownMenuBox(
                label = stringResource(R.string.apn_protocol),
                options = apnProtocolOptions,
@@ -234,6 +254,13 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState<ApnData>, uriInit: Ur
                selectedOptionIndex = apnData.apnRoaming,
                enabled = apnData.apnRoamingEnabled
            ) { apnData = apnData.copy(apnRoaming = it) }
            SettingsExposedDropdownMenuCheckBox(
                label = stringResource(R.string.network_type),
                options = getNetworkTypeDisplayNames(),
                selectedOptionsState = networkTypeSelectedOptionsState,
                emptyVal = stringResource(R.string.network_type_unspecified),
                enabled = apnData.networkTypeEnabled
            ) {}
            SwitchPreference(
                object : SwitchPreferenceModel {
                    override val title = context.resources.getString(R.string.carrier_enabled)
@@ -244,13 +271,6 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState<ApnData>, uriInit: Ur
                    }
                }
            )
            SettingsExposedDropdownMenuCheckBox(
                label = stringResource(R.string.network_type),
                options = getNetworkTypeDisplayNames(),
                selectedOptionsState = networkTypeSelectedOptionsState,
                emptyVal = stringResource(R.string.network_type_unspecified),
                enabled = apnData.networkTypeEnabled
            ) {}
        }
    }
}
 No newline at end of file
+6 −80
Original line number Diff line number Diff line
@@ -29,6 +29,12 @@ import androidx.compose.runtime.snapshots.SnapshotStateList
import com.android.internal.util.ArrayUtils
import com.android.settings.R
import com.android.settings.network.apn.ApnNetworkTypes.getNetworkType
import com.android.settings.network.apn.ApnTypes.APN_TYPES
import com.android.settings.network.apn.ApnTypes.APN_TYPE_ALL
import com.android.settings.network.apn.ApnTypes.APN_TYPE_EMERGENCY
import com.android.settings.network.apn.ApnTypes.APN_TYPE_IA
import com.android.settings.network.apn.ApnTypes.APN_TYPE_IMS
import com.android.settings.network.apn.ApnTypes.APN_TYPE_MCX
import java.util.Locale

data class ApnData(
@@ -113,67 +119,6 @@ data class CustomizedConfig(
    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.
 * @param arguments The data passed in when the user calls PageProvider.
@@ -483,25 +428,6 @@ fun hasAllApns(apnTypes: List<String>): Boolean {
private fun normalizeApnType(apnType: String): String =
    apnType.trim().lowercase(Locale.getDefault())

fun updateApnType(apnData: ApnData): String {
    return if (apnData.apnType == "" && apnData.customizedConfig.defaultApnTypes.isNotEmpty())
        getEditableApnType(apnData)
    else
        apnData.apnType
}

private fun getEditableApnType(apnData: ApnData): String {
    val customizedConfig = apnData.customizedConfig
    return customizedConfig.defaultApnTypes.filterNot { apnType ->
        customizedConfig.readOnlyApnTypes.contains(apnType) || apnType in listOf(
            APN_TYPE_IA,
            APN_TYPE_EMERGENCY,
            APN_TYPE_MCX,
            APN_TYPE_IMS,
        )
    }.joinToString()
}

fun deleteApn(uri: Uri, context: Context) {
    val contentResolver = context.contentResolver
    contentResolver.delete(uri, null, null)
+143 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.network.apn

import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.snapshots.SnapshotStateList

object ApnTypes {
    /**
     * 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"

    /** APN type for VSIM  */
    const val APN_TYPE_VSIM = "vsim"

    /** APN type for BIP  */
    const val APN_TYPE_BIP = "bip"

    /** APN type for ENTERPRISE  */
    const val APN_TYPE_ENTERPRISE = "enterprise"

    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,
        APN_TYPE_VSIM,
        APN_TYPE_BIP,
        APN_TYPE_ENTERPRISE
    )

    val APN_TYPES_OPTIONS = listOf(APN_TYPE_ALL) + APN_TYPES

    fun getApnTypeSelectedOptionsState(apnType: String): SnapshotStateList<Int> {
        val apnTypeSelectedOptionsState = mutableStateListOf<Int>()
        if (apnType.contains(APN_TYPE_ALL))
            APN_TYPES_OPTIONS.forEachIndexed { index, _ ->
                apnTypeSelectedOptionsState.add(index)
            }
        else {
            APN_TYPES_OPTIONS.forEachIndexed { index, type ->
                if (apnType.contains(type)) {
                    apnTypeSelectedOptionsState.add(index)
                }
            }
            if (apnTypeSelectedOptionsState.size == APN_TYPES.size)
                apnTypeSelectedOptionsState.add(APN_TYPES_OPTIONS.indexOf(APN_TYPE_ALL))
        }
        return apnTypeSelectedOptionsState
    }

    fun updateApnType(
        apnTypeSelectedOptionsState: SnapshotStateList<Int>,
        defaultApnTypes: List<String>,
        readOnlyApnTypes: List<String>
    ): String {
        val apnType = apnTypeSelectedOptionsState.joinToString { APN_TYPES_OPTIONS[it] }
        if (apnType.contains(APN_TYPE_ALL)) return APN_TYPE_ALL
        return if (apnType == "" && defaultApnTypes.isNotEmpty())
            getEditableApnType(defaultApnTypes, readOnlyApnTypes)
        else
            apnType
    }

    private fun getEditableApnType(
        defaultApnTypes: List<String>,
        readOnlyApnTypes: List<String>
    ): String {
        return defaultApnTypes.filterNot { apnType ->
            readOnlyApnTypes.contains(apnType) || apnType in listOf(
                APN_TYPE_IA,
                APN_TYPE_EMERGENCY,
                APN_TYPE_MCX,
                APN_TYPE_IMS,
            )
        }.joinToString()
    }
}
 No newline at end of file
+11 −11
Original line number Diff line number Diff line
@@ -50,9 +50,9 @@ class ApnEditPageProviderTest {

    private val context: Context = ApplicationProvider.getApplicationContext()
    private val apnName = "apn_name"
    private val mmsc = "mmsc"
    private val mmsProxy = "mms_proxy"
    private val apnType = "apn_type"
    private val proxy = "proxy"
    private val port = "port"
    private val apnType = context.resources.getString(R.string.apn_type)
    private val apnRoaming = "IPv4"
    private val apnEnable = context.resources.getString(R.string.carrier_enabled)
    private val apnProtocolOptions =
@@ -61,8 +61,8 @@ class ApnEditPageProviderTest {
    private val passwordTitle = context.resources.getString(R.string.apn_password)
    private val apnInit = ApnData(
        name = apnName,
        mmsc = mmsc,
        mmsProxy = mmsProxy,
        proxy = proxy,
        port = port,
        apnType = apnType,
        apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
        apnEnable = true
@@ -94,23 +94,23 @@ class ApnEditPageProviderTest {
    }

    @Test
    fun mmsc_displayed() {
    fun proxy_displayed() {
        composeTestRule.setContent {
            ApnPage(apnInit, remember { apnData }, uri)
        }
        composeTestRule.onRoot().onChild().onChildAt(0)
            .performScrollToNode(hasText(mmsc, true))
        composeTestRule.onNodeWithText(mmsc, true).assertIsDisplayed()
            .performScrollToNode(hasText(proxy, true))
        composeTestRule.onNodeWithText(proxy, true).assertIsDisplayed()
    }

    @Test
    fun mms_proxy_displayed() {
    fun port_displayed() {
        composeTestRule.setContent {
            ApnPage(apnInit, remember { apnData }, uri)
        }
        composeTestRule.onRoot().onChild().onChildAt(0)
            .performScrollToNode(hasText(mmsProxy, true))
        composeTestRule.onNodeWithText(mmsProxy, true).assertIsDisplayed()
            .performScrollToNode(hasText(port, true))
        composeTestRule.onNodeWithText(port, true).assertIsDisplayed()
    }

    @Test