Skip to content

Commit 7803e6c

Browse files
author
MasterDNS Dev
committed
fix(settings): restore GlobalSettingsScreen and re-add Version Info card correctly
1 parent e7b5cdb commit 7803e6c

1 file changed

Lines changed: 149 additions & 7 deletions

File tree

android/app/src/main/java/com/masterdns/vpn/ui/settings/GlobalSettingsScreen.kt

Lines changed: 149 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,14 @@ import androidx.compose.ui.text.font.FontWeight
5555
import androidx.compose.ui.text.style.TextDecoration
5656
import androidx.compose.ui.text.style.TextOverflow
5757
import androidx.compose.ui.unit.dp
58-
import androidx.compose.ui.text.style.TextAlign
59-
import com.masterdns.vpn.BuildConfig
6058
import androidx.compose.ui.window.Dialog
6159
import androidx.core.graphics.drawable.toBitmap
6260
import androidx.lifecycle.viewmodel.compose.viewModel
6361
import com.masterdns.vpn.R
6462
import com.masterdns.vpn.util.GlobalSettings
6563
import kotlinx.coroutines.launch
64+
import com.masterdns.vpn.BuildConfig
65+
6666
@OptIn(ExperimentalMaterial3Api::class)
6767
@Composable
6868
fun GlobalSettingsScreen(vm: GlobalSettingsViewModel = viewModel()) {
@@ -78,6 +78,7 @@ fun GlobalSettingsScreen(vm: GlobalSettingsViewModel = viewModel()) {
7878
val snackbarHostState = remember { SnackbarHostState() }
7979
val scope = rememberCoroutineScope()
8080
val uriHandler = LocalUriHandler.current
81+
8182
Scaffold(
8283
topBar = { TopAppBar(title = { Text("Settings") }) },
8384
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
@@ -118,14 +119,17 @@ fun GlobalSettingsScreen(vm: GlobalSettingsViewModel = viewModel()) {
118119
draft = draft.copy(connectionMode = mode)
119120
modeExpanded = false
120121
}
122+
)
121123
}
122124
}
123125
}
126+
124127
RowSwitch(
125128
title = "Split Tunneling",
126129
checked = draft.splitTunnelingEnabled,
127130
onChecked = { draft = draft.copy(splitTunnelingEnabled = it) }
128131
)
132+
129133
if (draft.splitTunnelingEnabled) {
130134
Card(
131135
onClick = {
@@ -134,23 +138,33 @@ fun GlobalSettingsScreen(vm: GlobalSettingsViewModel = viewModel()) {
134138
selectedQuery = ""
135139
activeTab = "AVAILABLE"
136140
showAppPicker = true
141+
},
137142
modifier = Modifier.fillMaxWidth()
138143
) {
139144
Column(modifier = Modifier.padding(12.dp)) {
140145
Text("Split Tunnel Apps")
141146
Text(
142147
"${parseCsv(draft.splitPackagesCsv).size} selected apps - tap to choose",
143148
style = MaterialTheme.typography.bodySmall
149+
)
150+
}
151+
}
152+
}
153+
144154
Button(
145155
onClick = {
146156
vm.save(normalize(draft))
147157
scope.launch { snackbarHostState.showSnackbar("Global settings saved and applied") }
148158
},
149159
modifier = Modifier.fillMaxWidth()
160+
) {
150161
Text("Save Global Settings")
162+
}
151163
}
152164
}
153165
}
166+
item {
167+
Card(colors = CardDefaults.cardColors()) {
154168
Column(
155169
modifier = Modifier.padding(12.dp),
156170
verticalArrangement = Arrangement.spacedBy(8.dp)
@@ -163,38 +177,84 @@ fun GlobalSettingsScreen(vm: GlobalSettingsViewModel = viewModel()) {
163177
title = "Main GitHub:",
164178
link = mainGithubLink,
165179
onOpen = { uriHandler.openUri("https://$mainGithubLink") }
180+
)
181+
LinkRow(
166182
title = "Main Telegram:",
167183
link = mainTelegramLink,
168184
onOpen = { uriHandler.openUri("https://$mainTelegramLink") }
185+
)
186+
LinkRow(
169187
title = "MDV-HN Android Client:",
170188
link = androidClientGithubLink,
171189
onOpen = { uriHandler.openUri("https://$androidClientGithubLink") }
190+
)
191+
}
192+
}
193+
}
194+
item {
172195
val engineVersion = stringResource(R.string.engine_version)
196+
Card(colors = CardDefaults.cardColors()) {
197+
Column(
173198
modifier = Modifier
174199
.fillMaxWidth()
175200
.padding(12.dp),
176-
verticalArrangement = Arrangement.spacedBy(4.dp)
201+
verticalArrangement = Arrangement.spacedBy(6.dp)
202+
) {
177203
Text("Version Info", style = MaterialTheme.typography.titleMedium)
178204
Row(
179205
modifier = Modifier.fillMaxWidth(),
180206
horizontalArrangement = Arrangement.SpaceBetween
181-
Text("App Version", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant)
182-
Text(BuildConfig.VERSION_NAME, style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Medium)
183-
Text("Upstream Engine", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant)
184-
Text(engineVersion, style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Medium)
207+
) {
208+
Text(
209+
"App Version",
210+
style = MaterialTheme.typography.bodyMedium,
211+
color = MaterialTheme.colorScheme.onSurfaceVariant
212+
)
213+
Text(
214+
BuildConfig.VERSION_NAME,
215+
style = MaterialTheme.typography.bodyMedium,
216+
fontWeight = FontWeight.Medium
217+
)
218+
}
219+
Row(
220+
modifier = Modifier.fillMaxWidth(),
221+
horizontalArrangement = Arrangement.SpaceBetween
222+
) {
223+
Text(
224+
"Upstream Engine",
225+
style = MaterialTheme.typography.bodyMedium,
226+
color = MaterialTheme.colorScheme.onSurfaceVariant
227+
)
228+
Text(
229+
engineVersion,
230+
style = MaterialTheme.typography.bodyMedium,
231+
fontWeight = FontWeight.Medium
232+
)
233+
}
234+
}
235+
}
236+
}
185237
}
186238
}
239+
187240
if (showAppPicker) {
188241
val selectedApps = installedApps.filter { draftAppSelection.contains(it.packageName) }
189242
val availableApps = installedApps.filterNot { draftAppSelection.contains(it.packageName) }
243+
190244
val selectedFiltered = selectedApps.filter {
191245
val q = selectedQuery.trim().lowercase()
192246
q.isEmpty() ||
193247
it.label.lowercase().contains(q) ||
194248
it.packageName.lowercase().contains(q)
195249
}.sortedWith(compareBy({ it.label.lowercase() }, { it.packageName }))
250+
196251
val availableFiltered = availableApps.filter {
197252
val q = availableQuery.trim().lowercase()
253+
q.isEmpty() ||
254+
it.label.lowercase().contains(q) ||
255+
it.packageName.lowercase().contains(q)
256+
}.sortedWith(compareBy({ it.label.lowercase() }, { it.packageName }))
257+
198258
Dialog(onDismissRequest = { showAppPicker = false }) {
199259
Surface(
200260
modifier = Modifier
@@ -215,54 +275,86 @@ fun GlobalSettingsScreen(vm: GlobalSettingsViewModel = viewModel()) {
215275
style = MaterialTheme.typography.bodySmall,
216276
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f)
217277
)
278+
218279
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
219280
FilterChip(
220281
selected = activeTab == "SELECTED",
221282
onClick = { activeTab = "SELECTED" },
222283
label = { Text("Selected ${selectedApps.size}") }
284+
)
285+
FilterChip(
223286
selected = activeTab == "AVAILABLE",
224287
onClick = { activeTab = "AVAILABLE" },
225288
label = { Text("Available ${availableApps.size}") }
289+
)
290+
}
291+
226292
if (activeTab == "SELECTED") {
227293
OutlinedTextField(
228294
value = selectedQuery,
229295
onValueChange = { selectedQuery = it },
230296
label = { Text("Search selected apps") },
297+
modifier = Modifier.fillMaxWidth()
298+
)
231299
} else {
300+
OutlinedTextField(
232301
value = availableQuery,
233302
onValueChange = { availableQuery = it },
234303
label = { Text("Search available apps") },
304+
modifier = Modifier.fillMaxWidth()
305+
)
306+
}
307+
308+
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
235309
OutlinedButton(
310+
onClick = {
236311
draftAppSelection = draftAppSelection.toMutableSet().apply {
237312
addAll(availableFiltered.map { it.packageName })
313+
}
314+
},
238315
modifier = Modifier.weight(1f)
316+
) {
239317
Text("Select Visible")
318+
}
319+
OutlinedButton(
240320
onClick = { draftAppSelection = mutableSetOf() },
321+
modifier = Modifier.weight(1f)
322+
) {
241323
Text("Select None")
324+
}
325+
}
326+
242327
Surface(
243328
modifier = Modifier.fillMaxWidth(),
244329
shape = RoundedCornerShape(12.dp),
245330
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f)
331+
) {
246332
Column(
247333
modifier = Modifier
248334
.fillMaxWidth()
249335
.padding(10.dp)
336+
) {
250337
val appsToShow = if (activeTab == "SELECTED") selectedFiltered else availableFiltered
251338
val emptyText = if (activeTab == "SELECTED") {
252339
"No selected app matches your search"
253340
} else {
254341
"No available app matches your search"
342+
}
343+
255344
Text(
256345
if (activeTab == "SELECTED") "Selected Apps" else "Available Apps",
257346
style = MaterialTheme.typography.labelLarge,
258347
color = MaterialTheme.colorScheme.primary
348+
)
349+
259350
if (appsToShow.isEmpty()) {
260351
Text(
261352
emptyText,
262353
style = MaterialTheme.typography.bodySmall,
263354
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f),
264355
modifier = Modifier.padding(top = 8.dp)
265356
)
357+
} else {
266358
LazyColumn(
267359
modifier = Modifier
268360
.fillMaxWidth()
@@ -279,14 +371,34 @@ fun GlobalSettingsScreen(vm: GlobalSettingsViewModel = viewModel()) {
279371
}
280372
)
281373
}
374+
}
375+
}
376+
}
377+
}
378+
282379
Row(
380+
modifier = Modifier.fillMaxWidth(),
283381
horizontalArrangement = Arrangement.End
382+
) {
284383
TextButton(onClick = { showAppPicker = false }) {
285384
Text("Cancel")
385+
}
386+
Button(
387+
onClick = {
286388
draft = draft.copy(splitPackagesCsv = draftAppSelection.sorted().joinToString(","))
287389
showAppPicker = false
390+
}
391+
) {
288392
Text("Apply")
393+
}
394+
}
395+
}
396+
}
397+
}
398+
}
289399
}
400+
401+
@Composable
290402
private fun LinkRow(title: String, link: String, onOpen: () -> Unit) {
291403
Column(
292404
modifier = Modifier
@@ -299,6 +411,7 @@ private fun LinkRow(title: String, link: String, onOpen: () -> Unit) {
299411
style = MaterialTheme.typography.bodyMedium,
300412
color = MaterialTheme.colorScheme.onSurfaceVariant
301413
)
414+
Text(
302415
text = link,
303416
style = MaterialTheme.typography.bodyMedium.copy(
304417
textDecoration = TextDecoration.Underline,
@@ -307,6 +420,11 @@ private fun LinkRow(title: String, link: String, onOpen: () -> Unit) {
307420
color = MaterialTheme.colorScheme.primary,
308421
maxLines = 3,
309422
overflow = TextOverflow.Ellipsis
423+
)
424+
}
425+
}
426+
427+
@Composable
310428
private fun AppRow(
311429
app: GlobalSettingsViewModel.AppEntry,
312430
checked: Boolean,
@@ -317,14 +435,19 @@ private fun AppRow(
317435
runCatching {
318436
context.packageManager.getApplicationIcon(app.packageName).toBitmap(48, 48)
319437
}.getOrNull()
438+
}
320439
Row(
440+
modifier = Modifier
441+
.fillMaxWidth()
321442
.clickable { onToggle() }
322443
.padding(vertical = 4.dp),
323444
horizontalArrangement = Arrangement.SpaceBetween,
324445
verticalAlignment = Alignment.CenterVertically
446+
) {
325447
Row(
326448
modifier = Modifier.weight(1f),
327449
verticalAlignment = Alignment.CenterVertically
450+
) {
328451
if (appIconBitmap != null) {
329452
Image(
330453
bitmap = appIconBitmap.asImageBitmap(),
@@ -334,23 +457,41 @@ private fun AppRow(
334457
} else {
335458
Icon(
336459
imageVector = Icons.Filled.ArrowDropDown,
460+
contentDescription = null,
461+
modifier = Modifier.size(24.dp)
462+
)
463+
}
337464
Spacer(modifier = Modifier.size(8.dp))
338465
Column {
339466
Text(text = app.label)
340467
Text(text = app.packageName)
468+
}
469+
}
341470
Checkbox(
342471
checked = checked,
343472
onCheckedChange = { onToggle() }
473+
)
474+
}
475+
}
476+
477+
@Composable
344478
private fun RowSwitch(title: String, checked: Boolean, onChecked: (Boolean) -> Unit) {
479+
Row(
345480
modifier = Modifier.fillMaxWidth(),
346481
horizontalArrangement = Arrangement.SpaceBetween
482+
) {
347483
Text(title)
348484
Switch(checked = checked, onCheckedChange = onChecked)
485+
}
486+
}
487+
349488
private fun parseCsv(value: String): Set<String> {
350489
return value.split(",")
351490
.map { it.trim() }
352491
.filter { it.isNotBlank() }
353492
.toSet()
493+
}
494+
354495
private fun normalize(settings: GlobalSettings): GlobalSettings {
355496
return settings.copy(
356497
connectionMode = settings.connectionMode.uppercase(),
@@ -361,3 +502,4 @@ private fun normalize(settings: GlobalSettings): GlobalSettings {
361502
.distinct()
362503
.joinToString(",")
363504
)
505+
}

0 commit comments

Comments
 (0)