mirror of
https://github.com/CherretGit/zaprett-app.git
synced 2025-12-10 05:29:37 +05:00
Compare commits
3 Commits
51356e63eb
...
bd1bdf8298
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd1bdf8298 | ||
|
|
adc98db3f1 | ||
|
|
9bbc3b771f |
@@ -3,5 +3,6 @@ package com.cherret.zaprett.data
|
|||||||
data class StrategyCheckResult (
|
data class StrategyCheckResult (
|
||||||
val path : String,
|
val path : String,
|
||||||
val progress : Float,
|
val progress : Float,
|
||||||
val status : Int
|
var domains: List<String>,
|
||||||
|
val status : StrategyTestingStatus,
|
||||||
)
|
)
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -2,17 +2,24 @@ package com.cherret.zaprett.ui.component
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
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.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
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.Icons
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
import androidx.compose.material.icons.filled.InstallMobile
|
import androidx.compose.material.icons.filled.InstallMobile
|
||||||
import androidx.compose.material.icons.filled.Update
|
import androidx.compose.material.icons.filled.Update
|
||||||
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.FilledTonalButton
|
import androidx.compose.material3.FilledTonalButton
|
||||||
@@ -26,8 +33,11 @@ import androidx.compose.material3.SnackbarHostState
|
|||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@@ -36,6 +46,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.cherret.zaprett.R
|
import com.cherret.zaprett.R
|
||||||
import com.cherret.zaprett.data.StrategyCheckResult
|
import com.cherret.zaprett.data.StrategyCheckResult
|
||||||
|
import com.cherret.zaprett.data.StrategyTestingStatus
|
||||||
import com.cherret.zaprett.ui.viewmodel.BaseRepoViewModel
|
import com.cherret.zaprett.ui.viewmodel.BaseRepoViewModel
|
||||||
import com.cherret.zaprett.utils.RepoItemInfo
|
import com.cherret.zaprett.utils.RepoItemInfo
|
||||||
import com.cherret.zaprett.utils.disableStrategy
|
import com.cherret.zaprett.utils.disableStrategy
|
||||||
@@ -184,9 +195,15 @@ fun RepoItem(
|
|||||||
@Composable
|
@Composable
|
||||||
fun StrategySelectionItem(strategy : StrategyCheckResult, prefs : SharedPreferences, context : Context, snackbarHostState : SnackbarHostState) {
|
fun StrategySelectionItem(strategy : StrategyCheckResult, prefs : SharedPreferences, context : Context, snackbarHostState : SnackbarHostState) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
var expanded by remember { mutableStateOf(false) }
|
||||||
ElevatedCard (
|
ElevatedCard (
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp),
|
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp),
|
||||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainer),
|
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainer),
|
||||||
|
onClick = {
|
||||||
|
if (strategy.status == StrategyTestingStatus.Completed) {
|
||||||
|
expanded = !expanded
|
||||||
|
}
|
||||||
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(start = 10.dp, end = 10.dp, top = 25.dp, bottom = 0.dp)
|
.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(
|
Icon(
|
||||||
imageVector = Icons.Default.Check,
|
imageVector = Icons.Default.Check,
|
||||||
@@ -223,7 +240,7 @@ fun StrategySelectionItem(strategy : StrategyCheckResult, prefs : SharedPreferen
|
|||||||
}
|
}
|
||||||
Row {
|
Row {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(strategy.status),
|
text = stringResource(strategy.status.resId),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f),
|
.weight(1f),
|
||||||
fontSize = 12.sp,
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@ import com.cherret.zaprett.R
|
|||||||
import com.cherret.zaprett.byedpi.ByeDpiVpnService
|
import com.cherret.zaprett.byedpi.ByeDpiVpnService
|
||||||
import com.cherret.zaprett.data.ServiceStatus
|
import com.cherret.zaprett.data.ServiceStatus
|
||||||
import com.cherret.zaprett.data.StrategyCheckResult
|
import com.cherret.zaprett.data.StrategyCheckResult
|
||||||
|
import com.cherret.zaprett.data.StrategyTestingStatus
|
||||||
import com.cherret.zaprett.utils.disableStrategy
|
import com.cherret.zaprett.utils.disableStrategy
|
||||||
import com.cherret.zaprett.utils.enableStrategy
|
import com.cherret.zaprett.utils.enableStrategy
|
||||||
import com.cherret.zaprett.utils.getActiveLists
|
import com.cherret.zaprett.utils.getActiveLists
|
||||||
@@ -26,6 +27,7 @@ import kotlinx.coroutines.awaitAll
|
|||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
@@ -57,8 +59,9 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
|
|||||||
strategyList.forEach { name ->
|
strategyList.forEach { name ->
|
||||||
strategyStates += StrategyCheckResult(
|
strategyStates += StrategyCheckResult(
|
||||||
path = name,
|
path = name,
|
||||||
status = R.string.strategy_status_waiting,
|
status = StrategyTestingStatus.Waiting,
|
||||||
progress = 0f
|
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
|
if (urls.isEmpty()) return@coroutineScope 0f
|
||||||
val results: List<Boolean> = urls.map { url ->
|
val results: List<String> = urls.map { url ->
|
||||||
async { testDomain(url) }
|
async { if (testDomain(url)) url else null }
|
||||||
}.awaitAll()
|
}.awaitAll().filterNotNull()
|
||||||
val successCount = results.count { it }
|
strategyStates[index].domains = results
|
||||||
(successCount.toFloat() / urls.size.toFloat()).coerceIn(0f, 1f)
|
(results.size.toFloat() / urls.size.toFloat()).coerceIn(0f, 1f)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun readActiveListsLines(): List<String> = withContext(Dispatchers.IO) {
|
suspend fun readActiveListsLines(): List<String> = withContext(Dispatchers.IO) {
|
||||||
@@ -104,17 +107,17 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
|
|||||||
val targets = readActiveListsLines()
|
val targets = readActiveListsLines()
|
||||||
for (index in strategyStates.indices) {
|
for (index in strategyStates.indices) {
|
||||||
val current = strategyStates[index]
|
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)
|
enableStrategy(current.path, prefs)
|
||||||
if (prefs.getBoolean("use_module", false)) {
|
if (prefs.getBoolean("use_module", false)) {
|
||||||
getStatus { if (it) stopService {} }
|
getStatus { if (it) stopService {} }
|
||||||
startService {}
|
startService {}
|
||||||
try {
|
try {
|
||||||
val progress = countReachable(targets)
|
val progress = countReachable(index, targets)
|
||||||
val old = strategyStates[index]
|
val old = strategyStates[index]
|
||||||
strategyStates[index] = old.copy(
|
strategyStates[index] = old.copy(
|
||||||
progress = progress,
|
progress = progress,
|
||||||
status = R.string.strategy_status_tested
|
status = StrategyTestingStatus.Completed
|
||||||
)
|
)
|
||||||
} finally {
|
} finally {
|
||||||
stopService {}
|
stopService {}
|
||||||
@@ -140,12 +143,12 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
|
|||||||
} ?: false
|
} ?: false
|
||||||
if (connected) delay(150L)
|
if (connected) delay(150L)
|
||||||
try {
|
try {
|
||||||
val progress = countReachable(targets)
|
val progress = countReachable(index,targets)
|
||||||
val old = strategyStates[index]
|
val old = strategyStates[index]
|
||||||
|
|
||||||
strategyStates[index] = old.copy(
|
strategyStates[index] = old.copy(
|
||||||
progress = progress,
|
progress = progress,
|
||||||
status = R.string.strategy_status_tested
|
status = StrategyTestingStatus.Completed
|
||||||
)
|
)
|
||||||
} finally {
|
} finally {
|
||||||
context.startService(Intent(context, ByeDpiVpnService::class.java).apply {
|
context.startService(Intent(context, ByeDpiVpnService::class.java).apply {
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ fun getZaprettPath(): String {
|
|||||||
|
|
||||||
fun getAllLists(): Array<String> {
|
fun getAllLists(): Array<String> {
|
||||||
val listsDir = File("${getZaprettPath()}/lists/include")
|
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 }
|
?.map { it.absolutePath }
|
||||||
?.toTypedArray()
|
?.toTypedArray()
|
||||||
?: emptyArray()
|
?: emptyArray()
|
||||||
@@ -106,7 +106,7 @@ fun getAllLists(): Array<String> {
|
|||||||
|
|
||||||
fun getAllIpsets(): Array<String> {
|
fun getAllIpsets(): Array<String> {
|
||||||
val listsDir = File("${getZaprettPath()}/ipset/include")
|
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 }
|
?.map { it.absolutePath }
|
||||||
?.toTypedArray()
|
?.toTypedArray()
|
||||||
?: emptyArray()
|
?: emptyArray()
|
||||||
@@ -114,7 +114,7 @@ fun getAllIpsets(): Array<String> {
|
|||||||
|
|
||||||
fun getAllExcludeLists(): Array<String> {
|
fun getAllExcludeLists(): Array<String> {
|
||||||
val listsDir = File("${getZaprettPath()}/lists/exclude/")
|
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 }
|
?.map { it.absolutePath }
|
||||||
?.toTypedArray()
|
?.toTypedArray()
|
||||||
?: emptyArray()
|
?: emptyArray()
|
||||||
@@ -122,7 +122,7 @@ fun getAllExcludeLists(): Array<String> {
|
|||||||
|
|
||||||
fun getAllExcludeIpsets(): Array<String> {
|
fun getAllExcludeIpsets(): Array<String> {
|
||||||
val listsDir = File("${getZaprettPath()}/ipset/exclude/")
|
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 }
|
?.map { it.absolutePath }
|
||||||
?.toTypedArray()
|
?.toTypedArray()
|
||||||
?: emptyArray()
|
?: emptyArray()
|
||||||
@@ -130,7 +130,7 @@ fun getAllExcludeIpsets(): Array<String> {
|
|||||||
|
|
||||||
fun getAllNfqwsStrategies(): Array<String> {
|
fun getAllNfqwsStrategies(): Array<String> {
|
||||||
val listsDir = File("${getZaprettPath()}/strategies/nfqws")
|
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 }
|
?.map { it.absolutePath }
|
||||||
?.toTypedArray()
|
?.toTypedArray()
|
||||||
?: emptyArray()
|
?: emptyArray()
|
||||||
@@ -138,7 +138,7 @@ fun getAllNfqwsStrategies(): Array<String> {
|
|||||||
|
|
||||||
fun getAllByeDPIStrategies(): Array<String> {
|
fun getAllByeDPIStrategies(): Array<String> {
|
||||||
val listsDir = File("${getZaprettPath()}/strategies/byedpi")
|
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 }
|
?.map { it.absolutePath }
|
||||||
?.toTypedArray()
|
?.toTypedArray()
|
||||||
?: emptyArray()
|
?: emptyArray()
|
||||||
|
|||||||
Reference in New Issue
Block a user