Skip to content

Commit 6a664a8

Browse files
authored
feat: add dialog to manually set any value (#96)
1 parent f516059 commit 6a664a8

5 files changed

Lines changed: 257 additions & 21 deletions

File tree

app/src/main/java/dev/bluehouse/enablevolte/Components.kt

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
package dev.bluehouse.enablevolte
22

3+
import androidx.compose.foundation.layout.Box
34
import androidx.compose.foundation.layout.Column
45
import androidx.compose.foundation.layout.Row
6+
import androidx.compose.foundation.layout.Spacer
7+
import androidx.compose.foundation.layout.defaultMinSize
58
import androidx.compose.foundation.layout.fillMaxWidth
69
import androidx.compose.foundation.layout.padding
710
import androidx.compose.foundation.layout.wrapContentHeight
811
import androidx.compose.foundation.layout.wrapContentWidth
12+
import androidx.compose.foundation.selection.selectableGroup
13+
import androidx.compose.foundation.text.KeyboardOptions
914
import androidx.compose.material3.AlertDialog
15+
import androidx.compose.material3.AlertDialogDefaults
16+
import androidx.compose.material3.Card
17+
import androidx.compose.material3.CardDefaults
1018
import androidx.compose.material3.DropdownMenuItem
1119
import androidx.compose.material3.ExperimentalMaterial3Api
1220
import androidx.compose.material3.ExposedDropdownMenuBox
1321
import androidx.compose.material3.ExposedDropdownMenuDefaults
1422
import androidx.compose.material3.MaterialTheme
23+
import androidx.compose.material3.RadioButton
1524
import androidx.compose.material3.Surface
1625
import androidx.compose.material3.Switch
1726
import androidx.compose.material3.Text
@@ -29,8 +38,11 @@ import androidx.compose.ui.Modifier
2938
import androidx.compose.ui.platform.LocalLifecycleOwner
3039
import androidx.compose.ui.res.stringResource
3140
import androidx.compose.ui.text.TextStyle
41+
import androidx.compose.ui.text.input.KeyboardType
3242
import androidx.compose.ui.unit.dp
3343
import androidx.compose.ui.unit.sp
44+
import androidx.compose.ui.window.Dialog
45+
import androidx.compose.ui.window.DialogProperties
3446
import androidx.lifecycle.Lifecycle
3547
import androidx.lifecycle.LifecycleEventObserver
3648
import androidx.lifecycle.LifecycleOwner
@@ -230,6 +242,122 @@ fun StringPropertyView(label: String, value: String?, onUpdate: ((String) -> Uni
230242
}
231243
}
232244

245+
enum class ValueType {
246+
Int,
247+
Long,
248+
Bool,
249+
String,
250+
IntArray,
251+
LongArray,
252+
BoolArray,
253+
StringArray,
254+
}
255+
256+
@OptIn(ExperimentalMaterial3Api::class)
257+
@Composable
258+
fun KeyValueEditView(label: String, onUpdate: ((String, ValueType?, String) -> Boolean)) {
259+
var configKey by rememberSaveable { mutableStateOf("") }
260+
var selectedValueType: ValueType? by rememberSaveable { mutableStateOf(null) }
261+
var value by rememberSaveable { mutableStateOf("") }
262+
var openEditPropertyDialog by rememberSaveable { mutableStateOf(false) }
263+
var dropdownExpanded by rememberSaveable { mutableStateOf(false) }
264+
265+
if (openEditPropertyDialog) {
266+
Dialog(
267+
onDismissRequest = { openEditPropertyDialog = false },
268+
properties = DialogProperties(),
269+
) {
270+
Card(
271+
colors = CardDefaults.cardColors(
272+
containerColor = AlertDialogDefaults.containerColor,
273+
contentColor = AlertDialogDefaults.textContentColor,
274+
),
275+
) {
276+
Column(modifier = Modifier.padding(20.dp)) {
277+
Text(stringResource(R.string.update_value), modifier = Modifier.padding(bottom = 16.dp), style = MaterialTheme.typography.titleLarge)
278+
TextField(
279+
value = configKey,
280+
label = { Text(stringResource(R.string.property_name)) },
281+
onValueChange = { configKey = it },
282+
)
283+
ExposedDropdownMenuBox(
284+
expanded = dropdownExpanded,
285+
onExpandedChange = { dropdownExpanded = !dropdownExpanded },
286+
modifier = Modifier.padding(bottom = 8.dp),
287+
) {
288+
TextField(
289+
// The `menuAnchor` modifier must be passed to the text field for correctness.
290+
modifier = Modifier.menuAnchor().wrapContentWidth(),
291+
readOnly = true,
292+
value = selectedValueType?.name ?: "",
293+
onValueChange = {},
294+
label = { Text(stringResource(R.string.property_type)) },
295+
trailingIcon = {
296+
ExposedDropdownMenuDefaults.TrailingIcon(expanded = dropdownExpanded)
297+
},
298+
colors = ExposedDropdownMenuDefaults.textFieldColors(),
299+
)
300+
ExposedDropdownMenu(
301+
expanded = dropdownExpanded,
302+
onDismissRequest = { dropdownExpanded = false },
303+
) {
304+
ValueType.values().forEach { valueType ->
305+
DropdownMenuItem(
306+
text = { Text(text = valueType.name) },
307+
onClick = {
308+
if (selectedValueType != valueType) {
309+
selectedValueType = valueType
310+
value = ""
311+
}
312+
dropdownExpanded = false
313+
},
314+
contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
315+
)
316+
}
317+
}
318+
}
319+
when (selectedValueType) {
320+
ValueType.Bool -> Row(
321+
modifier = Modifier.selectableGroup(),
322+
verticalAlignment = Alignment.CenterVertically,
323+
) {
324+
RadioButton(
325+
selected = value == "true",
326+
onClick = { value = "true" },
327+
)
328+
Text("true")
329+
RadioButton(
330+
selected = value == "false",
331+
onClick = { value = "false" },
332+
)
333+
Text("false")
334+
}
335+
ValueType.Int, ValueType.Long -> TextField(
336+
value = value,
337+
onValueChange = { value = it },
338+
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
339+
)
340+
is ValueType -> TextField(value = value, onValueChange = { value = it })
341+
else -> Box(modifier = Modifier.defaultMinSize(minHeight = 48.dp))
342+
}
343+
Row(modifier = Modifier.padding(top = 16.dp)) {
344+
Spacer(Modifier.weight(1f))
345+
TextButton(onClick = {
346+
if (onUpdate(configKey, selectedValueType, value)) {
347+
openEditPropertyDialog = false
348+
}
349+
}, enabled = selectedValueType != null) { Text(stringResource(R.string.confirm)) }
350+
TextButton(onClick = { openEditPropertyDialog = false }) { Text(stringResource(R.string.dismiss)) }
351+
}
352+
}
353+
}
354+
}
355+
}
356+
ClickablePropertyView(label = label, value = "") {
357+
openEditPropertyDialog = true
358+
}
359+
}
360+
233361
@OptIn(ExperimentalMaterial3Api::class)
234362
@Composable
235363
fun ClickablePropertyView(label: String, value: String?, onClick: (() -> Unit)? = null) {

app/src/main/java/dev/bluehouse/enablevolte/HomeActivity.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ import androidx.navigation.NavGraphBuilder
3838
import androidx.navigation.compose.NavHost
3939
import androidx.navigation.compose.composable
4040
import androidx.navigation.compose.currentBackStackEntryAsState
41+
import androidx.navigation.compose.navigation
4142
import androidx.navigation.compose.rememberNavController
4243
import dev.bluehouse.enablevolte.pages.Config
44+
import dev.bluehouse.enablevolte.pages.DumpedConfig
4345
import dev.bluehouse.enablevolte.pages.Home
4446
import dev.bluehouse.enablevolte.ui.theme.EnableVoLTETheme
4547
import org.lsposed.hiddenapibypass.HiddenApiBypass
@@ -90,8 +92,13 @@ fun PixelIMSApp() {
9092
Home(navController)
9193
}
9294
for (subscription in subscriptions) {
93-
composable("config${subscription.subscriptionId}") {
94-
Config(navController, subscription.subscriptionId)
95+
navigation(startDestination = "editConfig${subscription.subscriptionId}", route = "config${subscription.subscriptionId}") {
96+
composable("editConfig${subscription.subscriptionId}") {
97+
Config(navController, subscription.subscriptionId)
98+
}
99+
composable("dumpConfig${subscription.subscriptionId}") {
100+
DumpedConfig(subscription.subscriptionId)
101+
}
95102
}
96103
}
97104
}

app/src/main/java/dev/bluehouse/enablevolte/Moder.kt

Lines changed: 75 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -95,36 +95,49 @@ class CarrierModer(private val context: Context) : Moder() {
9595
}
9696

9797
class SubscriptionModer(val subscriptionId: Int) : Moder() {
98-
fun updateCarrierConfig(key: String, value: Boolean) {
99-
Log.d(TAG, "Setting $key to $value")
98+
private fun publishBundle(fn: (PersistableBundle) -> Unit) {
10099
val iCclInstance = this.loadCachedInterface { carrierConfigLoader }
101100
val overrideBundle = PersistableBundle()
102-
overrideBundle.putBoolean(key, value)
101+
fn(overrideBundle)
103102
iCclInstance.overrideConfig(this.subscriptionId, overrideBundle, true)
104103
}
104+
fun updateCarrierConfig(key: String, value: Boolean) {
105+
Log.d(TAG, "Setting $key to $value")
106+
publishBundle { it.putBoolean(key, value) }
107+
}
105108

106109
fun updateCarrierConfig(key: String, value: String) {
107110
Log.d(TAG, "Setting $key to $value")
108-
val iCclInstance = this.loadCachedInterface { carrierConfigLoader }
109-
val overrideBundle = PersistableBundle()
110-
overrideBundle.putString(key, value)
111-
iCclInstance.overrideConfig(this.subscriptionId, overrideBundle, true)
111+
publishBundle { it.putString(key, value) }
112112
}
113113

114114
fun updateCarrierConfig(key: String, value: Int) {
115115
Log.d(TAG, "Setting $key to $value")
116-
val iCclInstance = this.loadCachedInterface { carrierConfigLoader }
117-
val overrideBundle = PersistableBundle()
118-
overrideBundle.putInt(key, value)
119-
iCclInstance.overrideConfig(this.subscriptionId, overrideBundle, true)
116+
publishBundle { it.putInt(key, value) }
117+
}
118+
fun updateCarrierConfig(key: String, value: Long) {
119+
Log.d(TAG, "Setting $key to $value")
120+
publishBundle { it.putLong(key, value) }
120121
}
121122

122123
fun updateCarrierConfig(key: String, value: IntArray) {
123124
Log.d(TAG, "Setting $key to $value")
124-
val iCclInstance = this.loadCachedInterface { carrierConfigLoader }
125-
val overrideBundle = PersistableBundle()
126-
overrideBundle.putIntArray(key, value)
127-
iCclInstance.overrideConfig(this.subscriptionId, overrideBundle, true)
125+
publishBundle { it.putIntArray(key, value) }
126+
}
127+
128+
fun updateCarrierConfig(key: String, value: BooleanArray) {
129+
Log.d(TAG, "Setting $key to $value")
130+
publishBundle { it.putBooleanArray(key, value) }
131+
}
132+
133+
fun updateCarrierConfig(key: String, value: Array<String>) {
134+
Log.d(TAG, "Setting $key to $value")
135+
publishBundle { it.putStringArray(key, value) }
136+
}
137+
138+
fun updateCarrierConfig(key: String, value: LongArray) {
139+
Log.d(TAG, "Setting $key to $value")
140+
publishBundle { it.putLongArray(key, value) }
128141
}
129142

130143
fun clearCarrierConfig() {
@@ -138,7 +151,7 @@ class SubscriptionModer(val subscriptionId: Int) : Moder() {
138151
telephony.resetIms(sub.getSlotIndex(this.subscriptionId))
139152
}
140153

141-
private fun getStringValue(key: String): String {
154+
fun getStringValue(key: String): String {
142155
val subscriptionId = this.subscriptionId
143156
if (subscriptionId < 0) {
144157
return ""
@@ -149,7 +162,7 @@ class SubscriptionModer(val subscriptionId: Int) : Moder() {
149162
return config.getString(key)
150163
}
151164

152-
private fun getBooleanValue(key: String): Boolean {
165+
fun getBooleanValue(key: String): Boolean {
153166
val subscriptionId = this.subscriptionId
154167
if (subscriptionId < 0) {
155168
return false
@@ -160,7 +173,7 @@ class SubscriptionModer(val subscriptionId: Int) : Moder() {
160173
return config.getBoolean(key)
161174
}
162175

163-
private fun getIntValue(key: String): Int {
176+
fun getIntValue(key: String): Int {
164177
val subscriptionId = this.subscriptionId
165178
if (subscriptionId < 0) {
166179
return -1
@@ -171,7 +184,29 @@ class SubscriptionModer(val subscriptionId: Int) : Moder() {
171184
return config.getInt(key)
172185
}
173186

174-
private fun getIntArrayValue(key: String): IntArray {
187+
fun getLongValue(key: String): Long {
188+
val subscriptionId = this.subscriptionId
189+
if (subscriptionId < 0) {
190+
return -1
191+
}
192+
val iCclInstance = this.loadCachedInterface { carrierConfigLoader }
193+
194+
val config = iCclInstance.getConfigForSubId(subscriptionId, iCclInstance.defaultCarrierServicePackageName)
195+
return config.getLong(key)
196+
}
197+
198+
fun getBooleanArrayValue(key: String): BooleanArray {
199+
val subscriptionId = this.subscriptionId
200+
if (subscriptionId < 0) {
201+
return booleanArrayOf()
202+
}
203+
val iCclInstance = this.loadCachedInterface { carrierConfigLoader }
204+
205+
val config = iCclInstance.getConfigForSubId(subscriptionId, iCclInstance.defaultCarrierServicePackageName)
206+
return config.getBooleanArray(key)
207+
}
208+
209+
fun getIntArrayValue(key: String): IntArray {
175210
val subscriptionId = this.subscriptionId
176211
if (subscriptionId < 0) {
177212
return intArrayOf()
@@ -182,6 +217,27 @@ class SubscriptionModer(val subscriptionId: Int) : Moder() {
182217
return config.getIntArray(key)
183218
}
184219

220+
fun getStringArrayValue(key: String): Array<String> {
221+
val subscriptionId = this.subscriptionId
222+
if (subscriptionId < 0) {
223+
return arrayOf()
224+
}
225+
val iCclInstance = this.loadCachedInterface { carrierConfigLoader }
226+
227+
val config = iCclInstance.getConfigForSubId(subscriptionId, iCclInstance.defaultCarrierServicePackageName)
228+
return config.getStringArray(key)
229+
}
230+
fun getValue(key: String): Any? {
231+
val subscriptionId = this.subscriptionId
232+
if (subscriptionId < 0) {
233+
return null
234+
}
235+
val iCclInstance = this.loadCachedInterface { carrierConfigLoader }
236+
237+
val config = iCclInstance.getConfigForSubId(subscriptionId, iCclInstance.defaultCarrierServicePackageName)
238+
return config.get(key)
239+
}
240+
185241
val isVoLteConfigEnabled: Boolean
186242
get() = this.getBooleanValue(CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL)
187243

0 commit comments

Comments
 (0)