mirror of
https://github.com/CherretGit/zaprett-app.git
synced 2025-12-10 05:29:37 +05:00
add ipsets screen
This commit is contained in:
@@ -22,6 +22,7 @@ import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.Lan
|
||||
import androidx.compose.material.icons.filled.MultipleStop
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.SettingsInputComposite
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.NavigationBar
|
||||
@@ -49,6 +50,7 @@ import androidx.navigation.navArgument
|
||||
import com.cherret.zaprett.ui.screen.DebugScreen
|
||||
import com.cherret.zaprett.ui.screen.HomeScreen
|
||||
import com.cherret.zaprett.ui.screen.HostsScreen
|
||||
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
|
||||
@@ -66,9 +68,10 @@ sealed class Screen(val route: String, @StringRes val nameResId: Int, val icon:
|
||||
object home : Screen("home", R.string.title_home, Icons.Default.Home)
|
||||
object hosts : Screen("hosts", R.string.title_hosts, Icons.Default.Lan)
|
||||
object strategies : Screen("strategies", R.string.title_strategies, Icons.Default.MultipleStop)
|
||||
object ipsets : Screen("ipsets", R.string.title_ipset, Icons.Default.SettingsInputComposite)
|
||||
object settings : Screen("settings", R.string.title_settings, Icons.Default.Settings)
|
||||
}
|
||||
val topLevelRoutes = listOf(Screen.home, Screen.hosts, Screen.strategies, Screen.settings)
|
||||
val topLevelRoutes = listOf(Screen.home, Screen.hosts, Screen.strategies, Screen.ipsets, Screen.settings)
|
||||
val hideNavBar = listOf("repo?source={source}", "debugScreen")
|
||||
class MainActivity : ComponentActivity() {
|
||||
private val viewModel: HomeViewModel by viewModels()
|
||||
@@ -216,6 +219,7 @@ class MainActivity : ComponentActivity() {
|
||||
composable(Screen.home.route) { HomeScreen(viewModel = viewModel, vpnPermissionLauncher) }
|
||||
composable(Screen.hosts.route) { HostsScreen(navController) }
|
||||
composable(Screen.strategies.route) { StrategyScreen(navController) }
|
||||
composable(Screen.ipsets.route) { IpsetsScreen(navController) }
|
||||
composable(Screen.settings.route) { SettingsScreen(navController) }
|
||||
composable(route = "repo?source={source}",arguments = listOf(navArgument("source") {})) { backStackEntry ->
|
||||
val source = backStackEntry.arguments?.getString("source")
|
||||
|
||||
223
app/src/main/java/com/cherret/zaprett/ui/screen/IpsetsScreen.kt
Normal file
223
app/src/main/java/com/cherret/zaprett/ui/screen/IpsetsScreen.kt
Normal file
@@ -0,0 +1,223 @@
|
||||
package com.cherret.zaprett.ui.screen
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
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.Add
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
import androidx.compose.material.icons.filled.UploadFile
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SegmentedButton
|
||||
import androidx.compose.material3.SegmentedButtonDefaults
|
||||
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
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.platform.LocalContext
|
||||
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.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.cherret.zaprett.R
|
||||
import com.cherret.zaprett.ui.component.ListSwitchItem
|
||||
import com.cherret.zaprett.ui.viewmodel.HostsViewModel
|
||||
import com.cherret.zaprett.ui.viewmodel.IpsetViewModel
|
||||
import com.cherret.zaprett.utils.getHostListMode
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun IpsetsScreen(navController: NavController, viewModel: IpsetViewModel = viewModel()) {
|
||||
val context = LocalContext.current
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
val scope = rememberCoroutineScope()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val allLists = viewModel.allItems
|
||||
val checked = viewModel.checked
|
||||
val isRefreshing = viewModel.isRefreshing
|
||||
val filePickerLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.OpenDocument()
|
||||
) { uri ->
|
||||
uri?.let {
|
||||
if (getHostListMode(prefs) == "whitelist") viewModel.copySelectedFile(context, "/ipset/include", it)
|
||||
else viewModel.copySelectedFile(context, "/ipset/exclude", it) }
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.refresh()
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.title_ipset),
|
||||
fontSize = 40.sp,
|
||||
fontFamily = FontFamily(Font(R.font.unbounded, FontWeight.Normal))
|
||||
)
|
||||
},
|
||||
windowInsets = WindowInsets(0)
|
||||
)
|
||||
},
|
||||
content = { paddingValues ->
|
||||
PullToRefreshBox(
|
||||
isRefreshing = isRefreshing,
|
||||
onRefresh = {
|
||||
viewModel.refresh()
|
||||
},
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(
|
||||
top = paddingValues.calculateTopPadding(),
|
||||
bottom = paddingValues.calculateBottomPadding() + 80.dp
|
||||
),
|
||||
modifier = Modifier.navigationBarsPadding().fillMaxSize()
|
||||
) {
|
||||
item {
|
||||
IpsetTypeChoose(viewModel, prefs)
|
||||
}
|
||||
when {
|
||||
allLists.isEmpty() -> {
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier.fillParentMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.empty_list),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
items(allLists) { item ->
|
||||
ListSwitchItem (
|
||||
item = item,
|
||||
isChecked = checked[item] == true,
|
||||
onCheckedChange = { isChecked ->
|
||||
viewModel.onCheckedChange(item, isChecked, snackbarHostState, scope)
|
||||
},
|
||||
onDeleteClick = {
|
||||
viewModel.deleteItem(item, snackbarHostState, scope)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
floatingActionButton = {
|
||||
FloatingMenu(navController, filePickerLauncher)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Composable
|
||||
private fun FloatingMenu(navController: NavController, launcher: ActivityResultLauncher<Array<String>>) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
FloatingActionButton(
|
||||
modifier = Modifier.size(80.dp),
|
||||
onClick = { expanded = !expanded }
|
||||
) {
|
||||
Icon(Icons.Default.Add, contentDescription = stringResource(R.string.btn_add_host))
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.btn_download_host)) },
|
||||
onClick = {
|
||||
expanded = false
|
||||
navController.navigate("repo?source=hosts") { launchSingleTop = true }
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Download, contentDescription = stringResource(R.string.btn_download_host))
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.btn_add_host)) },
|
||||
onClick = {
|
||||
expanded = false
|
||||
addHost(launcher)
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.UploadFile, contentDescription = stringResource(R.string.btn_add_host))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@Composable
|
||||
fun IpsetTypeChoose(viewModel: IpsetViewModel, prefs : SharedPreferences) {
|
||||
val listType = remember { mutableStateOf(getHostListMode(prefs))}
|
||||
val options = listOf(stringResource(R.string.title_whitelist), stringResource(R.string.title_blacklist))
|
||||
val selectedIndex = if (listType.value == "whitelist") 0 else 1
|
||||
|
||||
SingleChoiceSegmentedButtonRow (
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
options.forEachIndexed { index, label ->
|
||||
SegmentedButton(
|
||||
shape = SegmentedButtonDefaults.itemShape(
|
||||
index = index,
|
||||
count = options.size
|
||||
),
|
||||
onClick = {
|
||||
listType.value = if (index == 0) "whitelist" else "blacklist"
|
||||
viewModel.setListType(listType.value)
|
||||
},
|
||||
selected = index == selectedIndex,
|
||||
label = {
|
||||
Text(
|
||||
label
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun addHost(launcher: ActivityResultLauncher<Array<String>>) {
|
||||
launcher.launch(arrayOf("text/plain"))
|
||||
}
|
||||
@@ -107,6 +107,7 @@ fun SettingsScreen(navController: NavController, viewModel : SettingsViewModel =
|
||||
val openNoModuleDialog = remember { mutableStateOf(false) }
|
||||
val showAboutDialog = remember { mutableStateOf(false) }
|
||||
val showHostsRepoUrlDialog = remember { mutableStateOf(false) }
|
||||
val showIpsetRepoUrlDialog = remember { mutableStateOf(false) }
|
||||
val showStrategyRepoUrlDialog = remember { mutableStateOf(false) }
|
||||
val showIPDialog = remember { mutableStateOf(false) }
|
||||
val showPortDialog = remember { mutableStateOf(false) }
|
||||
@@ -160,6 +161,13 @@ 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 = {
|
||||
@@ -167,6 +175,13 @@ fun SettingsScreen(navController: NavController, viewModel : SettingsViewModel =
|
||||
showHostsRepoUrlDialog.value = true
|
||||
}
|
||||
),
|
||||
Setting.Action(
|
||||
title = stringResource(R.string.ipset_repo_url),
|
||||
onClick = {
|
||||
textDialogValue.value = sharedPreferences.getString("ipset_repo_url", "https://raw.githubusercontent.com/CherretGit/zaprett-repo/refs/heads/main/ipsets.json") ?: "https://raw.githubusercontent.com/CherretGit/zaprett-repo/refs/heads/main/ipsets.json"
|
||||
showIpsetRepoUrlDialog.value = true
|
||||
}
|
||||
),
|
||||
Setting.Action(
|
||||
title = stringResource(R.string.btn_repository_url_strategies),
|
||||
onClick = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.cherret.zaprett.ui.viewmodel
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
@@ -25,7 +26,8 @@ import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
abstract class BaseRepoViewModel(application: Application) : AndroidViewModel(application) {
|
||||
val context = application.applicationContext
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
val context: Context = application.applicationContext
|
||||
val sharedPreferences: SharedPreferences = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
private val _errorFlow = MutableStateFlow<Throwable?>(null)
|
||||
val errorFlow: StateFlow<Throwable?> = _errorFlow
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.cherret.zaprett.ui.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import com.cherret.zaprett.utils.disableIpset
|
||||
import com.cherret.zaprett.utils.disableList
|
||||
import com.cherret.zaprett.utils.enableIpset
|
||||
import com.cherret.zaprett.utils.enableList
|
||||
import com.cherret.zaprett.utils.getActiveExcludeIpsets
|
||||
import com.cherret.zaprett.utils.getActiveExcludeLists
|
||||
import com.cherret.zaprett.utils.getAllExcludeIpsets
|
||||
import com.cherret.zaprett.utils.getAllIpsets
|
||||
import com.cherret.zaprett.utils.getHostListMode
|
||||
import com.cherret.zaprett.utils.getStatus
|
||||
import com.cherret.zaprett.utils.setHostListMode
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import java.io.File
|
||||
|
||||
class IpsetViewModel(application: Application): BaseListsViewModel(application) {
|
||||
private val sharedPreferences = application.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
override fun loadAllItems(): Array<String> =
|
||||
if (getHostListMode(sharedPreferences) == "whitelist") getAllIpsets()
|
||||
else getAllExcludeIpsets()
|
||||
override fun loadActiveItems(): Array<String> =
|
||||
if (getHostListMode(sharedPreferences) == "whitelist") getActiveExcludeIpsets(sharedPreferences)
|
||||
else getActiveExcludeLists(sharedPreferences)
|
||||
|
||||
override fun deleteItem(item: String, snackbarHostState: SnackbarHostState, scope: CoroutineScope) {
|
||||
val wasChecked = checked[item] == true
|
||||
disableList(item, sharedPreferences)
|
||||
val success = File(item).delete()
|
||||
if (success) refresh()
|
||||
if (sharedPreferences.getBoolean("use_module", false)) {
|
||||
getStatus { isEnabled ->
|
||||
if (isEnabled && wasChecked) {
|
||||
snackbarHostState.currentSnackbarData?.dismiss()
|
||||
showRestartSnackbar(context, snackbarHostState, scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCheckedChange(item: String, isChecked: Boolean, snackbarHostState: SnackbarHostState, scope: CoroutineScope) {
|
||||
checked[item] = isChecked
|
||||
if (isChecked) enableIpset(item, sharedPreferences) else disableIpset(item, sharedPreferences)
|
||||
if (sharedPreferences.getBoolean("use_module", false)) {
|
||||
getStatus { isEnabled ->
|
||||
if (isEnabled) {
|
||||
snackbarHostState.currentSnackbarData?.dismiss()
|
||||
showRestartSnackbar(
|
||||
context,
|
||||
snackbarHostState,
|
||||
scope
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fun setListType(type : String) {
|
||||
setHostListMode(sharedPreferences, type)
|
||||
refresh()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,7 +39,7 @@ fun getHostList(sharedPreferences: SharedPreferences, callback: (Result<List<Rep
|
||||
fun getIpsetList(sharedPreferences: SharedPreferences, callback: (Result<List<RepoItemInfo>>) -> Unit) {
|
||||
getRepo(
|
||||
sharedPreferences.getString(
|
||||
"hosts_repo_url",
|
||||
"ipset_repo_url",
|
||||
"https://raw.githubusercontent.com/CherretGit/zaprett-repo/refs/heads/main/ipsets.json"
|
||||
) ?: "https://raw.githubusercontent.com/CherretGit/zaprett-repo/refs/heads/main/ipsets.json",
|
||||
callback
|
||||
|
||||
@@ -164,6 +164,27 @@ fun getActiveLists(sharedPreferences: SharedPreferences): Array<String> {
|
||||
return sharedPreferences.getStringSet("lists", emptySet())?.toTypedArray() ?: emptyArray()
|
||||
}
|
||||
}
|
||||
fun getActiveIpsets(sharedPreferences: SharedPreferences): Array<String> {
|
||||
if (sharedPreferences.getBoolean("use_module", false)) {
|
||||
val configFile = getConfigFile()
|
||||
if (configFile.exists()) {
|
||||
val props = Properties()
|
||||
return try {
|
||||
FileInputStream(configFile).use { input ->
|
||||
props.load(input)
|
||||
}
|
||||
val activeLists = props.getProperty("active_ipsets", "")
|
||||
Log.d("Active ipsets", activeLists)
|
||||
if (activeLists.isNotEmpty()) activeLists.split(",")
|
||||
.toTypedArray() else emptyArray()
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
return emptyArray()
|
||||
}
|
||||
else return emptyArray()
|
||||
}
|
||||
fun getActiveExcludeLists(sharedPreferences: SharedPreferences): Array<String> {
|
||||
if (sharedPreferences.getBoolean("use_module", false)) {
|
||||
val configFile = getConfigFile()
|
||||
@@ -187,6 +208,28 @@ fun getActiveExcludeLists(sharedPreferences: SharedPreferences): Array<String> {
|
||||
}
|
||||
}
|
||||
|
||||
fun getActiveExcludeIpsets(sharedPreferences: SharedPreferences): Array<String> {
|
||||
if (sharedPreferences.getBoolean("use_module", false)) {
|
||||
val configFile = getConfigFile()
|
||||
if (configFile.exists()) {
|
||||
val props = Properties()
|
||||
return try {
|
||||
FileInputStream(configFile).use { input ->
|
||||
props.load(input)
|
||||
}
|
||||
val activeLists = props.getProperty("active_exclude_ipsets", "")
|
||||
Log.d("Active ipsets", activeLists)
|
||||
if (activeLists.isNotEmpty()) activeLists.split(",")
|
||||
.toTypedArray() else emptyArray()
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
return emptyArray()
|
||||
}
|
||||
else return emptyArray()
|
||||
}
|
||||
|
||||
fun getActiveNfqwsStrategies(): Array<String> {
|
||||
val configFile = File("${getZaprettPath()}/config")
|
||||
if (configFile.exists()) {
|
||||
@@ -265,7 +308,50 @@ fun enableList(path: String, sharedPreferences: SharedPreferences) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun enableIpset(path: String, sharedPreferences: SharedPreferences) {
|
||||
if (sharedPreferences.getBoolean("use_module", false)) {
|
||||
val configFile = getConfigFile()
|
||||
try {
|
||||
val props = Properties()
|
||||
if (configFile.exists()) {
|
||||
FileInputStream(configFile).use { input ->
|
||||
props.load(input)
|
||||
}
|
||||
}
|
||||
val activeLists = props.getProperty(
|
||||
if (getHostListMode(sharedPreferences) == "whitelist") "active_ipsets"
|
||||
else "active_exclude_ipsets",
|
||||
"")
|
||||
.split(",")
|
||||
.filter { it.isNotBlank() }
|
||||
.toMutableList()
|
||||
if (path !in activeLists) {
|
||||
activeLists.add(path)
|
||||
}
|
||||
props.setProperty(
|
||||
if (getHostListMode(sharedPreferences) == "whitelist") "active_ipsets"
|
||||
else "active_exclude_ipsets",
|
||||
activeLists.joinToString(",")
|
||||
)
|
||||
FileOutputStream(configFile).use { output ->
|
||||
props.store(output, "Don't place '/' in end of directory! Example: /sdcard")
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
else {
|
||||
val currentSet = sharedPreferences.getStringSet(
|
||||
if (getHostListMode(sharedPreferences) == "whitelist") "ipsets"
|
||||
else "exclude_ipsets", emptySet())?.toMutableSet() ?: mutableSetOf()
|
||||
if (path !in currentSet) {
|
||||
currentSet.add(path)
|
||||
sharedPreferences.edit { putStringSet(
|
||||
if (getHostListMode(sharedPreferences) == "whitelist") "ipsets"
|
||||
else "exclude_ipsetss", currentSet) }
|
||||
}
|
||||
}
|
||||
}
|
||||
fun enableStrategy(path: String, sharedPreferences: SharedPreferences) {
|
||||
if (sharedPreferences.getBoolean("use_module", false)) {
|
||||
val props = Properties()
|
||||
@@ -347,6 +433,57 @@ fun disableList(path: String, sharedPreferences: SharedPreferences) {
|
||||
}
|
||||
}
|
||||
|
||||
fun disableIpset(path: String, sharedPreferences: SharedPreferences) {
|
||||
if (sharedPreferences.getBoolean("use_module", false)) {
|
||||
val props = Properties()
|
||||
val configFile = getConfigFile()
|
||||
try {
|
||||
if (configFile.exists()) {
|
||||
FileInputStream(configFile).use { input ->
|
||||
props.load(input)
|
||||
}
|
||||
}
|
||||
val activeLists = props.getProperty(
|
||||
if (getHostListMode(sharedPreferences) == "whitelist") "active_ipsets"
|
||||
else "active_exclude_ipsets",
|
||||
"")
|
||||
.split(",")
|
||||
.filter { it.isNotBlank() }
|
||||
.toMutableList()
|
||||
if (path in activeLists) {
|
||||
activeLists.remove(path)
|
||||
}
|
||||
props.setProperty(
|
||||
if (getHostListMode(sharedPreferences) == "whitelist") "active_ipsets"
|
||||
else "active_exclude_ipsets",
|
||||
activeLists.joinToString(",")
|
||||
)
|
||||
FileOutputStream(configFile).use { output ->
|
||||
props.store(output, "Don't place '/' in end of directory! Example: /sdcard")
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
else {
|
||||
val currentSet = sharedPreferences.getStringSet(
|
||||
if (getHostListMode(sharedPreferences) == "whitelist") "ipsets"
|
||||
else "exclude_ipsets", emptySet())?.toMutableSet() ?: mutableSetOf()
|
||||
if (path in currentSet) {
|
||||
currentSet.remove(path)
|
||||
sharedPreferences.edit { putStringSet(
|
||||
if (getHostListMode(sharedPreferences) == "whitelist") "ipsets"
|
||||
else "exclude_ipsets", currentSet) }
|
||||
}
|
||||
if (currentSet.isEmpty()) {
|
||||
sharedPreferences.edit { remove(
|
||||
if (getHostListMode(sharedPreferences) == "whitelist") "ipsets"
|
||||
else "exclude_ipsets"
|
||||
) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun disableStrategy(path: String, sharedPreferences: SharedPreferences) {
|
||||
if (sharedPreferences.getBoolean("use_module", false)) {
|
||||
val props = Properties()
|
||||
|
||||
@@ -99,4 +99,5 @@
|
||||
<string name="btn_copy_log">Скопировать лог</string>
|
||||
<string name="log_copied">Лог скопирован</string>
|
||||
<string name="download_error">Произошла ошибка скачивания, пожалуйста, сообщите о ней разработчикам</string>
|
||||
<string name="ipset_repo_url">URL репозитория ipset</string>
|
||||
</resources>
|
||||
@@ -103,4 +103,6 @@
|
||||
<string name="btn_copy_log">Copy log</string>
|
||||
<string name="log_copied">Log copied</string>
|
||||
<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>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user