1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.network.apn
18 
19 import android.content.Context
20 import android.telephony.data.ApnSetting
21 import android.util.Log
22 import android.widget.Toast
23 import androidx.compose.runtime.mutableStateOf
24 import androidx.compose.ui.text.intl.Locale
25 import androidx.compose.ui.text.toLowerCase
26 import com.android.settings.R
27 import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption
28 
29 object ApnTypes {
30     private const val TAG = "ApnTypes"
31 
32     private val APN_TYPES = arrayOf(
33         ApnSetting.TYPE_DEFAULT_STRING,
34         ApnSetting.TYPE_MMS_STRING,
35         ApnSetting.TYPE_SUPL_STRING,
36         ApnSetting.TYPE_DUN_STRING,
37         ApnSetting.TYPE_HIPRI_STRING,
38         ApnSetting.TYPE_FOTA_STRING,
39         ApnSetting.TYPE_IMS_STRING,
40         ApnSetting.TYPE_CBS_STRING,
41         ApnSetting.TYPE_IA_STRING,
42         ApnSetting.TYPE_EMERGENCY_STRING,
43         ApnSetting.TYPE_MCX_STRING,
44         ApnSetting.TYPE_XCAP_STRING,
45         ApnSetting.TYPE_VSIM_STRING,
46         ApnSetting.TYPE_BIP_STRING,
47         ApnSetting.TYPE_ENTERPRISE_STRING,
48     )
49 
splitToListnull50     private fun splitToList(apnType: String): List<String> {
51         val types = apnType.split(',').map { it.trim().toLowerCase(Locale.current) }
52         if (ApnSetting.TYPE_ALL_STRING in types || APN_TYPES.all { it in types }) {
53             return listOf(ApnSetting.TYPE_ALL_STRING)
54         }
55         return APN_TYPES.filter { it in types }
56     }
57 
isApnTypeReadOnlynull58     fun isApnTypeReadOnly(apnType: String, readOnlyTypes: List<String>): Boolean {
59         val apnTypes = splitToList(apnType)
60         return ApnSetting.TYPE_ALL_STRING in readOnlyTypes ||
61             ApnSetting.TYPE_ALL_STRING in apnTypes && readOnlyTypes.isNotEmpty() ||
62             apnTypes.any { it in readOnlyTypes }
63     }
64 
<lambda>null65     fun getOptions(context: Context, apnType: String, readOnlyTypes: List<String>) = buildList {
66         val apnTypes = splitToList(apnType)
67         add(
68             context.createSettingsDropdownCheckOption(
69                 text = ApnSetting.TYPE_ALL_STRING,
70                 isSelectAll = true,
71                 changeable = readOnlyTypes.isEmpty(),
72                 selected = ApnSetting.TYPE_ALL_STRING in apnTypes,
73             )
74         )
75         for (type in APN_TYPES) {
76             add(
77                 context.createSettingsDropdownCheckOption(
78                     text = type,
79                     changeable = ApnSetting.TYPE_ALL_STRING !in readOnlyTypes &&
80                         type !in readOnlyTypes,
81                     selected = ApnSetting.TYPE_ALL_STRING in apnTypes || type in apnTypes,
82                 )
83             )
84         }
85     }.also { Log.d(TAG, "APN Type options: $it") }
86 
Contextnull87     private fun Context.createSettingsDropdownCheckOption(
88         text: String,
89         isSelectAll: Boolean = false,
90         changeable: Boolean,
91         selected: Boolean,
92     ) = SettingsDropdownCheckOption(
93         text = text,
94         isSelectAll = isSelectAll,
95         changeable = changeable,
96         selected = mutableStateOf(selected),
97     ) {
98         if (!changeable) {
99             val message = resources.getString(R.string.error_adding_apn_type, text)
100             Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
101         }
102     }
103 
<lambda>null104     fun List<SettingsDropdownCheckOption>.isValid(): Boolean = any { it.selected.value }
105 
toApnTypenull106     fun List<SettingsDropdownCheckOption>.toApnType(): String {
107         val (selectAllOptions, regularOptions) = partition { it.isSelectAll }
108         for (selectAllOption in selectAllOptions) {
109             if (selectAllOption.selected.value) return ApnSetting.TYPE_ALL_STRING
110         }
111         return regularOptions.filter { it.selected.value }.joinToString(",") { it.text }
112     }
113 
114     private val PreSelectedTypes = setOf(
115         ApnSetting.TYPE_DEFAULT_STRING,
116         ApnSetting.TYPE_MMS_STRING,
117         ApnSetting.TYPE_SUPL_STRING,
118         ApnSetting.TYPE_DUN_STRING,
119         ApnSetting.TYPE_HIPRI_STRING,
120         ApnSetting.TYPE_FOTA_STRING,
121         ApnSetting.TYPE_CBS_STRING,
122         ApnSetting.TYPE_XCAP_STRING,
123     )
124 
getPreSelectedApnTypenull125     fun getPreSelectedApnType(customizedConfig: CustomizedConfig): String =
126         (customizedConfig.defaultApnTypes
127             ?: defaultPreSelectedApnTypes(customizedConfig.readOnlyApnTypes))
128             .joinToString(",")
129 
130     private fun defaultPreSelectedApnTypes(readOnlyApnTypes: List<String>) =
131         if (ApnSetting.TYPE_ALL_STRING in readOnlyApnTypes) emptyList()
132         else PreSelectedTypes.filterNot { it in readOnlyApnTypes }
133 }
134