add automatic strategy selection, remove phantom switch in settings, add ipset repository url setting, made some refactoring, disable analytics and autoupdate in debug releases by default, etc

This commit is contained in:
white
2025-10-12 19:01:22 +03:00
parent 9208885de4
commit 0b69d2bf4d
15 changed files with 541 additions and 36 deletions

View File

@@ -29,6 +29,12 @@ android {
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
buildConfigField("boolean", "send_firebase_analytics", "true")
buildConfigField("boolean", "auto_update", "true")
}
debug {
buildConfigField("boolean", "send_firebase_analytics", "false")
buildConfigField("boolean", "auto_update", "false")
}
}
compileOptions {

View File

@@ -54,6 +54,7 @@ import com.cherret.zaprett.ui.screen.IpsetsScreen
import com.cherret.zaprett.ui.screen.RepoScreen
import com.cherret.zaprett.ui.screen.SettingsScreen
import com.cherret.zaprett.ui.screen.StrategyScreen
import com.cherret.zaprett.ui.screen.StrategySelectionScreen
import com.cherret.zaprett.ui.theme.ZaprettTheme
import com.cherret.zaprett.ui.viewmodel.HomeViewModel
import com.cherret.zaprett.ui.viewmodel.HostRepoViewModel
@@ -72,7 +73,7 @@ sealed class Screen(val route: String, @StringRes val nameResId: Int, val icon:
object settings : Screen("settings", R.string.title_settings, Icons.Default.Settings)
}
val topLevelRoutes = listOf(Screen.home, Screen.hosts, Screen.strategies, Screen.ipsets, Screen.settings)
val hideNavBar = listOf("repo?source={source}", "debugScreen")
val hideNavBar = listOf("repo?source={source}", "debugScreen", "selectionScreen")
class MainActivity : ComponentActivity() {
private val viewModel: HomeViewModel by viewModels()
private lateinit var notificationPermissionLauncher: ActivityResultLauncher<String>
@@ -127,7 +128,7 @@ class MainActivity : ComponentActivity() {
)
}
var showWelcomeDialog by remember { mutableStateOf(sharedPreferences.getBoolean("welcome_dialog", true)) }
firebaseAnalytics.setAnalyticsCollectionEnabled(sharedPreferences.getBoolean("send_firebase_analytics", true))
firebaseAnalytics.setAnalyticsCollectionEnabled(sharedPreferences.getBoolean("send_firebase_analytics", BuildConfig.send_firebase_analytics))
BottomBar()
if (showStoragePermissionDialog) {
PermissionDialog(
@@ -239,6 +240,7 @@ class MainActivity : ComponentActivity() {
}
}
composable("debugScreen") { DebugScreen(navController) }
composable("selectionScreen") { StrategySelectionScreen(navController) }
}
}
}

View File

@@ -15,6 +15,7 @@ import com.cherret.zaprett.MainActivity
import com.cherret.zaprett.R
import com.cherret.zaprett.data.ServiceStatus
import com.cherret.zaprett.utils.disableList
import com.cherret.zaprett.utils.getActiveByeDPIStrategyContent
import com.cherret.zaprett.utils.getActiveExcludeIpsets
import com.cherret.zaprett.utils.getActiveExcludeLists
import com.cherret.zaprett.utils.getActiveIpsets
@@ -191,7 +192,13 @@ class ByeDpiVpnService : VpnService() {
val listSet = if (getHostListMode(sharedPreferences) == "whitelist") getActiveLists(sharedPreferences) else getActiveExcludeLists(sharedPreferences)
val ipsetSet = if (getHostListMode(sharedPreferences) == "whitelist") getActiveIpsets(sharedPreferences) else getActiveExcludeIpsets(sharedPreferences)
CoroutineScope(Dispatchers.IO).launch {
val args = parseArgs(socksIp, socksPort, getActiveStrategy(sharedPreferences), prepareList(listSet), prepareIpset(ipsetSet), sharedPreferences)
val args = parseArgs(
socksIp,
socksPort,
getActiveByeDPIStrategyContent(sharedPreferences),
prepareList(listSet),
prepareIpset(ipsetSet),
sharedPreferences)
val result = NativeBridge().jniStartProxy(args)
if (result < 0) {
Log.d("proxy","Failed to start byedpi proxy")

View File

@@ -0,0 +1,7 @@
package com.cherret.zaprett.data
data class StrategyCheckResult (
val path : String,
val progress : Float,
val status : Int
)

View File

@@ -1,5 +1,7 @@
package com.cherret.zaprett.ui.component
import android.content.Context
import android.content.SharedPreferences
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -7,26 +9,37 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
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.CardDefaults
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.FilledTonalIconButton
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProgressIndicatorDefaults
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
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.ui.viewmodel.BaseRepoViewModel
import com.cherret.zaprett.utils.RepoItemInfo
import com.cherret.zaprett.utils.enableStrategy
import kotlinx.coroutines.launch
@Composable
fun ListSwitchItem(item: String, isChecked: Boolean, onCheckedChange: (Boolean) -> Unit, onDeleteClick: () -> Unit) {
@@ -164,3 +177,80 @@ fun RepoItem(
}
}
}
@Composable
fun StrategySelectionItem(strategy : StrategyCheckResult, prefs : SharedPreferences, context : Context, snackbarHostState : SnackbarHostState) {
val scope = rememberCoroutineScope()
ElevatedCard (
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainer),
modifier = Modifier
.fillMaxWidth()
.padding(start = 10.dp, end = 10.dp, top = 25.dp, bottom = 0.dp)
) {
Column (
Modifier
.fillMaxWidth()
.padding(16.dp)
)
{
Row {
Text(
text = strategy.path,
modifier = Modifier
.weight(1f)
)
FilledTonalIconButton(
onClick = {
enableStrategy(strategy.path, prefs)
scope.launch {
snackbarHostState.showSnackbar(
message = context.getString(R.string.strategy_applied)
)
}
},
enabled = strategy.status == R.string.strategy_status_tested
) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = "apply"
)
}
}
Row {
Text(
text = stringResource(strategy.status),
modifier = Modifier
.weight(1f),
fontSize = 12.sp,
)
}
Row (
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
LinearProgressIndicator(
modifier = Modifier
.weight(1f),
progress = {
strategy.progress
},
color = ProgressIndicatorDefaults.linearColor,
trackColor = ProgressIndicatorDefaults.linearTrackColor,
strokeCap = ProgressIndicatorDefaults.LinearStrokeCap,
)
Text(
text = "${(strategy.progress*100).toInt()}%",
modifier = Modifier
.padding(start = 16.dp),
)
}
}
}
}

View File

@@ -100,8 +100,8 @@ fun SettingsScreen(navController: NavController, viewModel : SettingsViewModel =
val useModule = viewModel.useModule.collectAsState()
val updateOnBoot = remember { mutableStateOf(sharedPreferences.getBoolean("update_on_boot", true)) }
val autoRestart = viewModel.autoRestart.collectAsState()
val autoUpdate = remember { mutableStateOf(sharedPreferences.getBoolean("auto_update", true)) }
val sendFirebaseAnalytics = remember { mutableStateOf(sharedPreferences.getBoolean("send_firebase_analytics", true)) }
val autoUpdate = remember { mutableStateOf(sharedPreferences.getBoolean("auto_update", BuildConfig.auto_update)) }
val sendFirebaseAnalytics = remember { mutableStateOf(sharedPreferences.getBoolean("send_firebase_analytics", BuildConfig.send_firebase_analytics)) }
val ipv6 = remember { mutableStateOf(sharedPreferences.getBoolean("ipv6",false)) }
val openNoRootDialog = remember { mutableStateOf(false) }
val openNoModuleDialog = remember { mutableStateOf(false) }
@@ -117,6 +117,7 @@ fun SettingsScreen(navController: NavController, viewModel : SettingsViewModel =
val showBlackDialog = remember { mutableStateOf(false) }
val showAppsListsSheet = remember { mutableStateOf(false) }
val showSystemApps = remember { mutableStateOf(sharedPreferences.getBoolean("show_system_apps", false)) }
val showChangeProbeTimeout = remember { mutableStateOf(false) }
val settingsList = listOf(
Setting.Section(stringResource(R.string.general_section)),
@@ -155,13 +156,6 @@ fun SettingsScreen(navController: NavController, viewModel : SettingsViewModel =
editor.putBoolean("send_firebase_analytics", it).apply()
}
),
Setting.Toggle(
title = "",
checked = false,
onToggle = {
}
),
Setting.Action(
title = stringResource(R.string.btn_repository_url_lists),
onClick = {
@@ -183,9 +177,7 @@ fun SettingsScreen(navController: NavController, viewModel : SettingsViewModel =
showStrategyRepoUrlDialog.value = true
}
),
Setting.Section(
title = stringResource(R.string.shared_section)
),
Setting.Section(title = stringResource(R.string.shared_section)),
Setting.Action(
title = stringResource(R.string.btn_applist),
onClick = {
@@ -204,6 +196,20 @@ fun SettingsScreen(navController: NavController, viewModel : SettingsViewModel =
showBlackDialog.value = true
}
),
Setting.Section(stringResource(R.string.title_selection)),
Setting.Action(
title = stringResource(R.string.begin_selection),
onClick = {
navController.navigate("selectionScreen")
}
),
Setting.Action(
title = stringResource(R.string.change_probe_timeout),
onClick = {
textDialogValue.value = sharedPreferences.getLong("probe_timeout", 1000L).toString()
showChangeProbeTimeout.value = true
}
),
Setting.Section(stringResource(R.string.byedpi_section)),
Setting.Toggle(
title = stringResource(R.string.btn_ipv6),
@@ -234,9 +240,7 @@ fun SettingsScreen(navController: NavController, viewModel : SettingsViewModel =
showDNSDialog.value = true
}
),
Setting.Section(
title = stringResource(R.string.zapret_section)
),
Setting.Section(title = stringResource(R.string.zapret_section)),
Setting.Toggle(
title = stringResource(R.string.btn_autorestart),
checked = autoRestart.value,
@@ -271,6 +275,11 @@ fun SettingsScreen(navController: NavController, viewModel : SettingsViewModel =
editor.putString("hosts_repo_url", it).apply()
}, onDismiss = { showHostsRepoUrlDialog.value = false })
}
if (showIpsetRepoUrlDialog.value) {
TextDialog(stringResource(R.string.btn_repository_url_ipsets), stringResource(R.string.hint_enter_repository_url_ipsets), textDialogValue.value, onConfirm = {
editor.putString("ipsets_repo_url", it).apply()
}, onDismiss = { showIpsetRepoUrlDialog.value = false })
}
if (showStrategyRepoUrlDialog.value) {
TextDialog(stringResource(R.string.btn_repository_url_strategies), stringResource(R.string.hint_enter_repository_url_strategies), textDialogValue.value, onConfirm = {
@@ -332,6 +341,12 @@ fun SettingsScreen(navController: NavController, viewModel : SettingsViewModel =
)
}
if (showChangeProbeTimeout.value) {
TextDialog(stringResource(R.string.probe_timeout), stringResource(R.string.hint_enter_probe_timeout), textDialogValue.value, onConfirm = {
editor.putLong("probe_timeout", it.toLong()).apply()
}, onDismiss = { showChangeProbeTimeout.value = false })
}
Scaffold(
topBar = {
TopAppBar(

View File

@@ -0,0 +1,164 @@
package com.cherret.zaprett.ui.screen
import android.content.Context.MODE_PRIVATE
import android.util.Log
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.sp
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Info
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.TextButton
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.cherret.zaprett.R
import com.cherret.zaprett.ui.component.StrategySelectionItem
import com.cherret.zaprett.ui.viewmodel.StrategySelectionViewModel
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun StrategySelectionScreen(navController: NavController, viewModel : StrategySelectionViewModel = viewModel()){
val snackbarHostState = remember { SnackbarHostState() }
val strategyStates = viewModel.strategyStates
val context = LocalContext.current
val prefs = context.getSharedPreferences("settings", MODE_PRIVATE)
var showDialog = remember { mutableStateOf(false) }
if (showDialog.value) {
InfoAlert { showDialog.value = false }
}
Scaffold(
topBar = {
TopAppBar(
title = {
Text(
text = stringResource(R.string.title_selection),
fontSize = 40.sp,
fontFamily = FontFamily(Font(R.font.unbounded, FontWeight.Normal))
)
},
navigationIcon = {
IconButton(onClick = { navController.popBackStack() }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringResource(R.string.btn_back)
)
}
},
actions = {
IconButton(
onClick = {
showDialog.value = true
}
) {
Icon(
imageVector = Icons.Default.Info,
contentDescription = "info"
)
}
},
windowInsets = WindowInsets(0)
)
},
snackbarHost = { SnackbarHost(snackbarHostState) },
content = { paddingValues ->
LazyColumn (
modifier = Modifier
.padding(paddingValues)
) {
item {
Row (
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
)
{
FilledTonalButton(
onClick = {
viewModel.viewModelScope.launch {
snackbarHostState.showSnackbar(
context.getString(R.string.begin_selection_snack)
)
viewModel.performTest()
}
}
) {
Text(stringResource(R.string.begin_selection))
}
}
}
when {
strategyStates.isEmpty() -> {
item {
Box(
modifier = Modifier.fillParentMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
stringResource(R.string.empty_list),
textAlign = TextAlign.Center
)
}
}
}
else -> {
items(strategyStates, key = { it.path }) { item ->
StrategySelectionItem(item, prefs, context, snackbarHostState)
}
}
}
}
}
)
}
@Composable
fun InfoAlert(onDismiss: () -> Unit) {
AlertDialog(
title = { Text(text = stringResource(R.string.strategy_selection_info_title)) },
text = { Text(text = stringResource(R.string.strategy_selection_info_msg)) },
onDismissRequest = onDismiss,
confirmButton = {
TextButton(onClick = onDismiss) {
Text(stringResource(R.string.btn_continue))
}
}
)
}

View File

@@ -14,6 +14,7 @@ import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.core.content.ContextCompat
import androidx.lifecycle.AndroidViewModel
import com.cherret.zaprett.BuildConfig
import com.cherret.zaprett.R
import com.cherret.zaprett.byedpi.ByeDpiVpnService
import com.cherret.zaprett.data.ServiceStatus
@@ -71,7 +72,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
var showUpdateDialog = mutableStateOf(false)
fun checkForUpdate() {
if (prefs.getBoolean("auto_update", true)) {
if (prefs.getBoolean("auto_update", BuildConfig.auto_update)) {
getUpdate(prefs) {
if (it != null) {
downloadUrl.value = it.downloadUrl.toString()

View File

@@ -42,7 +42,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
init {
refreshApplications()
_useModule.value = context.getSharedPreferences("settings", MODE_PRIVATE).getBoolean("use_module", false)
getStartOnBoot { value ->
getStartOnBoot(prefs) { value ->
_autoRestart.value = value
}
}
@@ -155,7 +155,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
fun handleAutoRestart(context: Context) {
val sharedPreferences = context.getSharedPreferences("settings", MODE_PRIVATE)
if (sharedPreferences.getBoolean("use_module", false)) {
setStartOnBoot{ value ->
setStartOnBoot(prefs) { value ->
_autoRestart.value = value
}
}

View File

@@ -0,0 +1,170 @@
package com.cherret.zaprett.ui.viewmodel
import android.app.Application
import android.content.Context.MODE_PRIVATE
import android.content.Intent
import android.util.Log
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
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.utils.disableStrategy
import com.cherret.zaprett.utils.enableStrategy
import com.cherret.zaprett.utils.getActiveLists
import com.cherret.zaprett.utils.getActiveStrategy
import com.cherret.zaprett.utils.getAllNfqwsStrategies
import com.cherret.zaprett.utils.getAllStrategies
import com.cherret.zaprett.utils.getStatus
import com.cherret.zaprett.utils.startService
import com.cherret.zaprett.utils.stopService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.File
import java.util.concurrent.TimeUnit
class StrategySelectionViewModel(application: Application) : AndroidViewModel(application) {
val prefs = application.getSharedPreferences("settings", MODE_PRIVATE)
val client = OkHttpClient.Builder()
.callTimeout(prefs.getLong("probe_timeout", 1000L), TimeUnit.MILLISECONDS)
.build()
val context = application
val strategyStates = mutableStateListOf<StrategyCheckResult>()
init {
loadStrategies()
}
fun loadStrategies() {
val strategyList = getAllStrategies(prefs)
strategyStates.clear()
strategyList.forEach { name ->
strategyStates += StrategyCheckResult(
path = name,
status = R.string.strategy_status_waiting,
progress = 0f
)
}
}
suspend fun testDomain(domain : String) : Boolean = withContext(Dispatchers.IO) {
val request = Request.Builder()
.url("https://${domain}")
.build()
try {
client.newCall(request).execute().use { response ->
response.isSuccessful || (response.code in 300..399)
}
} catch (e: Exception) {
false
}
}
suspend fun countReachable(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)
}
suspend fun readActiveListsLines(): List<String> = withContext(Dispatchers.IO) {
val result = mutableListOf<String>()
getActiveLists(prefs).forEach { path ->
runCatching {
File(path).useLines { lines ->
lines.forEach { line ->
result += line
}
}
}.onFailure {
Log.e("Error", "Occured error when creating list for check")
}
}
result
}
suspend fun performTest() {
val targets = readActiveListsLines()
for (index in strategyStates.indices) {
val current = strategyStates[index]
strategyStates[index] = current.copy(status = R.string.strategy_status_testing)
enableStrategy(current.path, prefs)
if (prefs.getBoolean("use_module", false)) {
getStatus { if (it) stopService {} }
startService {}
try {
val progress = countReachable(targets)
val old = strategyStates[index]
strategyStates[index] = old.copy(
progress = progress,
status = R.string.strategy_status_tested
)
} finally {
stopService {}
disableStrategy(current.path, prefs)
}
}
else {
if (ByeDpiVpnService.status == ServiceStatus.Connected) {
context.startService(Intent(context, ByeDpiVpnService::class.java).apply {
action = "STOP_VPN"
})
delay(300L)
}
context.startService(Intent(context, ByeDpiVpnService::class.java).apply {
action = "START_VPN"
})
val connected = withTimeoutOrNull(10_000L) {
while (ByeDpiVpnService.status != ServiceStatus.Connected) {
delay(100L)
}
true
} ?: false
if (connected) delay(150L)
try {
val progress = countReachable(targets)
val old = strategyStates[index]
strategyStates[index] = old.copy(
progress = progress,
status = R.string.strategy_status_tested
)
} finally {
context.startService(Intent(context, ByeDpiVpnService::class.java).apply {
action = "STOP_VPN"
})
delay(200L)
disableStrategy(current.path, prefs)
}
}
}
val sorted = strategyStates.sortedByDescending { it.progress }
strategyStates.clear()
strategyStates.addAll(sorted)
}
}

View File

@@ -8,8 +8,8 @@ import com.cherret.zaprett.byedpi.ByeDpiVpnService
import com.cherret.zaprett.data.ServiceStatus
import com.cherret.zaprett.utils.disableStrategy
import com.cherret.zaprett.utils.enableStrategy
import com.cherret.zaprett.utils.getActiveByeDPIStrategies
import com.cherret.zaprett.utils.getActiveNfqwsStrategies
import com.cherret.zaprett.utils.getActiveByeDPIStrategy
import com.cherret.zaprett.utils.getActiveNfqwsStrategy
import com.cherret.zaprett.utils.getAllByeDPIStrategies
import com.cherret.zaprett.utils.getAllNfqwsStrategies
import com.cherret.zaprett.utils.getStatus
@@ -84,10 +84,10 @@ interface StrategyProvider {
class NfqwsStrategyProvider : StrategyProvider {
override fun getAll() = getAllNfqwsStrategies()
override fun getActive() = getActiveNfqwsStrategies()
override fun getActive() = getActiveNfqwsStrategy()
}
class ByeDPIStrategyProvider(private val sharedPreferences: SharedPreferences) : StrategyProvider {
override fun getAll() = getAllByeDPIStrategies()
override fun getActive() = getActiveByeDPIStrategies(sharedPreferences)
override fun getActive() = getActiveByeDPIStrategy(sharedPreferences)
}

View File

@@ -64,16 +64,20 @@ fun getConfigFile(): File {
return File(Environment.getExternalStorageDirectory(), "zaprett/config")
}
fun setStartOnBoot(callback: (Boolean) -> Unit) {
Shell.cmd("zaprett autostart").submit { result ->
if (result.out.isNotEmpty() && result.out.toString().contains("true")) callback(true) else callback(false)
fun setStartOnBoot(prefs : SharedPreferences, callback: (Boolean) -> Unit) {
if (prefs.getBoolean("use_module", false)) {
Shell.cmd("zaprett autostart").submit { result ->
if (result.out.isNotEmpty() && result.out.toString().contains("true")) callback(true) else callback(false)
}
}
}
fun getStartOnBoot(callback: (Boolean) -> Unit) {
Shell.cmd("zaprett get-autostart").submit { result ->
if (result.out.isNotEmpty() && result.out.toString().contains("true")) callback(true) else callback(false)
}
fun getStartOnBoot(prefs : SharedPreferences, callback: (Boolean) -> Unit) {
if (prefs.getBoolean("use_module", false)) {
Shell.cmd("zaprett get-autostart").submit { result ->
if (result.out.isNotEmpty() && result.out.toString().contains("true")) callback(true) else callback(false)
}
} else { callback(false) }
}
fun getZaprettPath(): String {
@@ -140,6 +144,11 @@ fun getAllByeDPIStrategies(): Array<String> {
?: emptyArray()
}
fun getAllStrategies(sharedPreferences : SharedPreferences) : Array<String> {
return if (sharedPreferences.getBoolean("use_module", false)) getAllNfqwsStrategies()
else getAllByeDPIStrategies()
}
fun getActiveLists(sharedPreferences: SharedPreferences): Array<String> {
if (sharedPreferences.getBoolean("use_module", false)) {
@@ -230,7 +239,7 @@ fun getActiveExcludeIpsets(sharedPreferences: SharedPreferences): Array<String>
else return emptyArray()
}
fun getActiveNfqwsStrategies(): Array<String> {
fun getActiveNfqwsStrategy(): Array<String> {
val configFile = File("${getZaprettPath()}/config")
if (configFile.exists()) {
val props = Properties()
@@ -248,7 +257,7 @@ fun getActiveNfqwsStrategies(): Array<String> {
return emptyArray()
}
fun getActiveByeDPIStrategies(sharedPreferences: SharedPreferences): Array<String> {
fun getActiveByeDPIStrategy(sharedPreferences: SharedPreferences): Array<String> {
val path = sharedPreferences.getString("active_strategy", "")
if (!path.isNullOrBlank() && File(path).exists()) {
return arrayOf(path)
@@ -256,7 +265,7 @@ fun getActiveByeDPIStrategies(sharedPreferences: SharedPreferences): Array<Strin
return emptyArray()
}
fun getActiveStrategy(sharedPreferences: SharedPreferences): List<String> {
fun getActiveByeDPIStrategyContent(sharedPreferences: SharedPreferences): List<String> {
val path = sharedPreferences.getString("active_strategy", "")
if (!path.isNullOrBlank() && File(path).exists()) {
return File(path).readLines()
@@ -264,6 +273,12 @@ fun getActiveStrategy(sharedPreferences: SharedPreferences): List<String> {
return emptyList()
}
fun getActiveStrategy(sharedPreferences: SharedPreferences): Array<String> {
return if (sharedPreferences.getBoolean("use_module", false)) getActiveNfqwsStrategy()
else getActiveByeDPIStrategy(sharedPreferences)
}
fun enableList(path: String, sharedPreferences: SharedPreferences) {
if (sharedPreferences.getBoolean("use_module", false)) {
val configFile = getConfigFile()

View File

@@ -100,4 +100,18 @@
<string name="log_copied">Лог скопирован</string>
<string name="download_error">Произошла ошибка скачивания, пожалуйста, сообщите о ней разработчикам</string>
<string name="ipset_repo_url">URL репозитория ipset</string>
<string name="title_selection">Подбор стратегии</string>
<string name="btn_repository_url_ipsets">URL репозитория ipset</string>
<string name="hint_enter_repository_url_ipsets">Введите URL репозитория ipset</string>
<string name="strategy_status_waiting">ожидание...</string>
<string name="strategy_status_testing">проверка...</string>
<string name="strategy_status_tested">проверка завершена</string>
<string name="strategy_applied">Стратегия применена</string>
<string name="begin_selection">Начать подбор</string>
<string name="begin_selection_snack">Начат подбор стратегии. Не закрывайте эту вкладку</string>
<string name="change_probe_timeout">Изменить таймаут пробы</string>
<string name="probe_timeout">Таймаут пробы</string>
<string name="hint_enter_probe_timeout">Введите таймаут пробы</string>
<string name="strategy_selection_info_title">Информация</string>
<string name="strategy_selection_info_msg">"В этом разделе настроек приложения представлен перебор стратегий\n Подбор проходит среди скачанных стратегий, поэтому заранее скачайте из репозитория или добавьте из файловой системы интересующие вас стратегии для сравнения. \n Перед началом так же выберете один или несколько листов доменов на вкладке \"Листы\", затем нажмите на \"Начать подбор\". Не используйте для перебора списки с большим количеством доменов."</string>
</resources>

View File

@@ -105,4 +105,18 @@
<string name="download_error">Occurred a file download error, please report to developers</string>
<string name="ipset_repo_url">Ipset repository URL</string>
<string name="title_ipset" translatable="false">Ipset</string>
<string name="title_selection">Strategy selection</string>
<string name="btn_repository_url_ipsets">Ipsets repository url</string>
<string name="hint_enter_repository_url_ipsets">Enter ipsets repository URL</string>
<string name="strategy_status_waiting">waiting...</string>
<string name="strategy_status_testing">testing...</string>
<string name="strategy_status_tested">testing ended</string>
<string name="strategy_applied">Strategy applied</string>
<string name="begin_selection">Begin selection</string>
<string name="begin_selection_snack">Strategy selection has begun. Please keep this tab open.</string>
<string name="change_probe_timeout">Change probe timeout</string>
<string name="probe_timeout">Probe timeout</string>
<string name="hint_enter_probe_timeout">Enter probe timeout</string>
<string name="strategy_selection_info_title">Tip</string>
<string name="strategy_selection_info_msg">This section of the application settings allows you to iterate through strategies.\n The selection is based on downloaded strategies, so download the strategies you\'re interested in from the repository or add them from the file system for comparison.\n Before starting, select one or more domain lists in the \"Lists\" tab, then click \"Start selection\". Avoid using lists with a large number of domains.</string>
</resources>

View File

@@ -1,5 +1,5 @@
[versions]
agp = "8.12.0"
agp = "8.13.0"
kotlin = "2.2.10"
coreKtx = "1.17.0"
junit = "4.13.2"