3 Commits

Author SHA1 Message Date
CherretGit
bd1bdf8298 Merge remote-tracking branch 'origin/main' 2025-10-18 01:56:19 +07:00
CherretGit
adc98db3f1 add info in strategy selection 2025-10-18 01:56:11 +07:00
CherretGit
9bbc3b771f filter list/ipset/strategy files to only .txt 2025-10-17 15:37:59 +07:00
5 changed files with 79 additions and 22 deletions

View File

@@ -3,5 +3,6 @@ package com.cherret.zaprett.data
data class StrategyCheckResult (
val path : String,
val progress : Float,
val status : Int
var domains: List<String>,
val status : StrategyTestingStatus,
)

View File

@@ -0,0 +1,7 @@
package com.cherret.zaprett.data
import com.cherret.zaprett.R
enum class StrategyTestingStatus(val resId: Int) {
Waiting(R.string.strategy_status_waiting), Testing(R.string.strategy_status_testing), Completed(R.string.strategy_status_tested)
}

View File

@@ -2,17 +2,24 @@ package com.cherret.zaprett.ui.component
import android.content.Context
import android.content.SharedPreferences
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.InstallMobile
import androidx.compose.material.icons.filled.Update
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.FilledTonalButton
@@ -26,8 +33,11 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -36,6 +46,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.cherret.zaprett.R
import com.cherret.zaprett.data.StrategyCheckResult
import com.cherret.zaprett.data.StrategyTestingStatus
import com.cherret.zaprett.ui.viewmodel.BaseRepoViewModel
import com.cherret.zaprett.utils.RepoItemInfo
import com.cherret.zaprett.utils.disableStrategy
@@ -184,9 +195,15 @@ fun RepoItem(
@Composable
fun StrategySelectionItem(strategy : StrategyCheckResult, prefs : SharedPreferences, context : Context, snackbarHostState : SnackbarHostState) {
val scope = rememberCoroutineScope()
var expanded by remember { mutableStateOf(false) }
ElevatedCard (
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainer),
onClick = {
if (strategy.status == StrategyTestingStatus.Completed) {
expanded = !expanded
}
},
modifier = Modifier
.fillMaxWidth()
.padding(start = 10.dp, end = 10.dp, top = 25.dp, bottom = 0.dp)
@@ -213,7 +230,7 @@ fun StrategySelectionItem(strategy : StrategyCheckResult, prefs : SharedPreferen
)
}
},
enabled = strategy.status == R.string.strategy_status_tested
enabled = strategy.status == StrategyTestingStatus.Completed
) {
Icon(
imageVector = Icons.Default.Check,
@@ -223,7 +240,7 @@ fun StrategySelectionItem(strategy : StrategyCheckResult, prefs : SharedPreferen
}
Row {
Text(
text = stringResource(strategy.status),
text = stringResource(strategy.status.resId),
modifier = Modifier
.weight(1f),
fontSize = 12.sp,
@@ -255,5 +272,34 @@ fun StrategySelectionItem(strategy : StrategyCheckResult, prefs : SharedPreferen
)
}
}
AnimatedVisibility(
visible = expanded,
enter = expandVertically(),
exit = shrinkVertically()
) {
Card (
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainer),
modifier = Modifier
.padding(horizontal = 8.dp)
.fillMaxWidth()
) {
LazyColumn(modifier = Modifier.heightIn(max = 300.dp)) {
items(strategy.domains) { item ->
Card(
elevation = CardDefaults.cardElevation(4.dp),
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp)
) {
Text(
text = item,
modifier = Modifier.padding(16.dp),
style = MaterialTheme.typography.bodyLarge
)
}
}
}
}
}
}
}

View File

@@ -12,6 +12,7 @@ import com.cherret.zaprett.R
import com.cherret.zaprett.byedpi.ByeDpiVpnService
import com.cherret.zaprett.data.ServiceStatus
import com.cherret.zaprett.data.StrategyCheckResult
import com.cherret.zaprett.data.StrategyTestingStatus
import com.cherret.zaprett.utils.disableStrategy
import com.cherret.zaprett.utils.enableStrategy
import com.cherret.zaprett.utils.getActiveLists
@@ -26,6 +27,7 @@ import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
@@ -57,8 +59,9 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
strategyList.forEach { name ->
strategyStates += StrategyCheckResult(
path = name,
status = R.string.strategy_status_waiting,
progress = 0f
status = StrategyTestingStatus.Waiting,
progress = 0f,
domains = emptyList()
)
}
}
@@ -76,13 +79,13 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
}
}
suspend fun countReachable(urls: List<String>): Float = coroutineScope {
suspend fun countReachable(index: Int, urls: List<String>): Float = coroutineScope {
if (urls.isEmpty()) return@coroutineScope 0f
val results: List<Boolean> = urls.map { url ->
async { testDomain(url) }
}.awaitAll()
val successCount = results.count { it }
(successCount.toFloat() / urls.size.toFloat()).coerceIn(0f, 1f)
val results: List<String> = urls.map { url ->
async { if (testDomain(url)) url else null }
}.awaitAll().filterNotNull()
strategyStates[index].domains = results
(results.size.toFloat() / urls.size.toFloat()).coerceIn(0f, 1f)
}
suspend fun readActiveListsLines(): List<String> = withContext(Dispatchers.IO) {
@@ -104,17 +107,17 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
val targets = readActiveListsLines()
for (index in strategyStates.indices) {
val current = strategyStates[index]
strategyStates[index] = current.copy(status = R.string.strategy_status_testing)
strategyStates[index] = current.copy(status = StrategyTestingStatus.Testing)
enableStrategy(current.path, prefs)
if (prefs.getBoolean("use_module", false)) {
getStatus { if (it) stopService {} }
startService {}
try {
val progress = countReachable(targets)
val progress = countReachable(index, targets)
val old = strategyStates[index]
strategyStates[index] = old.copy(
progress = progress,
status = R.string.strategy_status_tested
status = StrategyTestingStatus.Completed
)
} finally {
stopService {}
@@ -140,12 +143,12 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
} ?: false
if (connected) delay(150L)
try {
val progress = countReachable(targets)
val progress = countReachable(index,targets)
val old = strategyStates[index]
strategyStates[index] = old.copy(
progress = progress,
status = R.string.strategy_status_tested
status = StrategyTestingStatus.Completed
)
} finally {
context.startService(Intent(context, ByeDpiVpnService::class.java).apply {

View File

@@ -98,7 +98,7 @@ fun getZaprettPath(): String {
fun getAllLists(): Array<String> {
val listsDir = File("${getZaprettPath()}/lists/include")
return listsDir.listFiles { file -> file.isFile }
return listsDir.listFiles { file -> file.isFile && file.extension.lowercase() == "txt" }
?.map { it.absolutePath }
?.toTypedArray()
?: emptyArray()
@@ -106,7 +106,7 @@ fun getAllLists(): Array<String> {
fun getAllIpsets(): Array<String> {
val listsDir = File("${getZaprettPath()}/ipset/include")
return listsDir.listFiles { file -> file.isFile }
return listsDir.listFiles { file -> file.isFile && file.extension.lowercase() == "txt" }
?.map { it.absolutePath }
?.toTypedArray()
?: emptyArray()
@@ -114,7 +114,7 @@ fun getAllIpsets(): Array<String> {
fun getAllExcludeLists(): Array<String> {
val listsDir = File("${getZaprettPath()}/lists/exclude/")
return listsDir.listFiles { file -> file.isFile }
return listsDir.listFiles { file -> file.isFile && file.extension.lowercase() == "txt" }
?.map { it.absolutePath }
?.toTypedArray()
?: emptyArray()
@@ -122,7 +122,7 @@ fun getAllExcludeLists(): Array<String> {
fun getAllExcludeIpsets(): Array<String> {
val listsDir = File("${getZaprettPath()}/ipset/exclude/")
return listsDir.listFiles { file -> file.isFile }
return listsDir.listFiles { file -> file.isFile && file.extension.lowercase() == "txt" }
?.map { it.absolutePath }
?.toTypedArray()
?: emptyArray()
@@ -130,7 +130,7 @@ fun getAllExcludeIpsets(): Array<String> {
fun getAllNfqwsStrategies(): Array<String> {
val listsDir = File("${getZaprettPath()}/strategies/nfqws")
return listsDir.listFiles { file -> file.isFile }
return listsDir.listFiles { file -> file.isFile && file.extension.lowercase() == "txt" }
?.map { it.absolutePath }
?.toTypedArray()
?: emptyArray()
@@ -138,7 +138,7 @@ fun getAllNfqwsStrategies(): Array<String> {
fun getAllByeDPIStrategies(): Array<String> {
val listsDir = File("${getZaprettPath()}/strategies/byedpi")
return listsDir.listFiles { file -> file.isFile }
return listsDir.listFiles { file -> file.isFile && file.extension.lowercase() == "txt" }
?.map { it.absolutePath }
?.toTypedArray()
?: emptyArray()