2 Commits

7 changed files with 171 additions and 5 deletions

View File

@@ -1,7 +1,11 @@
package com.cherret.zaprett.ui.screen
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.SharedPreferences
import android.os.Build
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
@@ -79,11 +83,43 @@ fun HostsScreen(navController: NavController, viewModel: HostsViewModel = viewMo
if (getHostListMode(prefs) == "whitelist") viewModel.copySelectedFile(context, "/lists/include", it)
else viewModel.copySelectedFile(context, "/lists/exclude", it) }
}
val error by viewModel.errorFlow.collectAsState()
LaunchedEffect(Unit) {
viewModel.refresh()
}
if (error.isNotEmpty()) {
AlertDialog(
onDismissRequest = {
viewModel.clearError()
},
title = { Text(stringResource(R.string.error_text)) },
text = {
Text(stringResource(R.string.error_unknown))
},
dismissButton = {
TextButton(onClick = {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip: ClipData = ClipData.newPlainText("Error log", error)
clipboard.setPrimaryClip(clip)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S) {
Toast.makeText(context, context.getString(R.string.log_copied), Toast.LENGTH_SHORT).show()
}
}) {
Text(stringResource(R.string.btn_copy_log))
}
},
confirmButton = {
TextButton(onClick = {
viewModel.clearError()
}) {
Text(stringResource(R.string.btn_continue))
}
}
)
}
Scaffold(
topBar = {
TopAppBar(

View File

@@ -1,7 +1,11 @@
package com.cherret.zaprett.ui.screen
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.SharedPreferences
import android.os.Build
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
@@ -79,11 +83,43 @@ fun IpsetsScreen(navController: NavController, viewModel: IpsetViewModel = viewM
if (getHostListMode(prefs) == "whitelist") viewModel.copySelectedFile(context, "/ipset/include", it)
else viewModel.copySelectedFile(context, "/ipset/exclude", it) }
}
val error by viewModel.errorFlow.collectAsState()
LaunchedEffect(Unit) {
viewModel.refresh()
}
if (error.isNotEmpty()) {
AlertDialog(
onDismissRequest = {
viewModel.clearError()
},
title = { Text(stringResource(R.string.error_text)) },
text = {
Text(stringResource(R.string.error_unknown))
},
dismissButton = {
TextButton(onClick = {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip: ClipData = ClipData.newPlainText("Error log", error)
clipboard.setPrimaryClip(clip)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S) {
Toast.makeText(context, context.getString(R.string.log_copied), Toast.LENGTH_SHORT).show()
}
}) {
Text(stringResource(R.string.btn_copy_log))
}
},
confirmButton = {
TextButton(onClick = {
viewModel.clearError()
}) {
Text(stringResource(R.string.btn_continue))
}
}
)
}
Scaffold(
topBar = {
TopAppBar(

View File

@@ -1,6 +1,10 @@
package com.cherret.zaprett.ui.screen
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Build
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
@@ -73,11 +77,43 @@ fun StrategyScreen(navController: NavController, viewModel: StrategyViewModel =
it
) }
}
val error by viewModel.errorFlow.collectAsState()
LaunchedEffect(Unit) {
viewModel.refresh()
}
if (error.isNotEmpty()) {
AlertDialog(
onDismissRequest = {
viewModel.clearError()
},
title = { Text(stringResource(R.string.error_text)) },
text = {
Text(stringResource(R.string.error_unknown))
},
dismissButton = {
TextButton(onClick = {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip: ClipData = ClipData.newPlainText("Error log", error)
clipboard.setPrimaryClip(clip)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S) {
Toast.makeText(context, context.getString(R.string.log_copied), Toast.LENGTH_SHORT).show()
}
}) {
Text(stringResource(R.string.btn_copy_log))
}
},
confirmButton = {
TextButton(onClick = {
viewModel.clearError()
}) {
Text(stringResource(R.string.btn_continue))
}
}
)
}
Scaffold(
topBar = {
TopAppBar(

View File

@@ -1,8 +1,13 @@
package com.cherret.zaprett.ui.screen
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.content.Intent
import android.net.VpnService
import android.os.Build
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -59,6 +64,7 @@ fun StrategySelectionScreen(navController: NavController, vpnLauncher: ActivityR
val prefs = context.getSharedPreferences("settings", MODE_PRIVATE)
var showDialog = remember { mutableStateOf(false) }
val requestVpnPermission by viewModel.requestVpnPermission.collectAsState()
val error by viewModel.errorFlow.collectAsState()
if (showDialog.value) {
InfoAlert { showDialog.value = false }
@@ -76,6 +82,37 @@ fun StrategySelectionScreen(navController: NavController, vpnLauncher: ActivityR
}
}
if (error.isNotEmpty()) {
AlertDialog(
onDismissRequest = {
viewModel.clearError()
},
title = { Text(stringResource(R.string.error_text)) },
text = {
Text(stringResource(R.string.error_unknown))
},
dismissButton = {
TextButton(onClick = {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip: ClipData = ClipData.newPlainText("Error log", error)
clipboard.setPrimaryClip(clip)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S) {
Toast.makeText(context, context.getString(R.string.log_copied), Toast.LENGTH_SHORT).show()
}
}) {
Text(stringResource(R.string.btn_copy_log))
}
},
confirmButton = {
TextButton(onClick = {
viewModel.clearError()
}) {
Text(stringResource(R.string.btn_continue))
}
}
)
}
Scaffold(
topBar = {
TopAppBar(

View File

@@ -22,6 +22,7 @@ import com.cherret.zaprett.utils.restartService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import java.io.File
import java.io.FileOutputStream
@@ -37,6 +38,9 @@ abstract class BaseListsViewModel(application: Application) : AndroidViewModel(a
var isRefreshing by mutableStateOf(false)
private set
private val _errorFlow = MutableStateFlow("")
val errorFlow = _errorFlow.asStateFlow()
private var _showNoPermissionDialog = MutableStateFlow(false)
val showNoPermissionDialog: StateFlow<Boolean> = _showNoPermissionDialog
@@ -72,12 +76,18 @@ abstract class BaseListsViewModel(application: Application) : AndroidViewModel(a
actionLabel = context.getString(R.string.btn_restart_service)
)
if (result == SnackbarResult.ActionPerformed) {
restartService {}
restartService { error ->
_errorFlow.value = error
}
snackbarHostState.showSnackbar(context.getString(R.string.snack_reload))
}
}
}
fun clearError() {
_errorFlow.value = ""
}
fun copySelectedFile(context: Context, path: String, uri: Uri) {
//if (!Environment.isExternalStorageManager()) return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){

View File

@@ -43,6 +43,8 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
val context = application
private val _requestVpnPermission = MutableStateFlow(false)
val requestVpnPermission = _requestVpnPermission.asStateFlow()
private val _errorFlow = MutableStateFlow("")
val errorFlow = _errorFlow.asStateFlow()
val strategyStates = mutableStateListOf<StrategyCheckResult>()
var noHostsCard = mutableStateOf(false)
private set
@@ -125,8 +127,12 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
strategyStates[index] = current.copy(status = StrategyTestingStatus.Testing)
enableStrategy(current.path, prefs)
if (prefs.getBoolean("use_module", false)) {
getStatus { if (it) stopService {} }
startService {}
getStatus { if (it) stopService { error ->
_errorFlow.value = error
} }
startService { error ->
_errorFlow.value = error
}
try {
val progress = countReachable(index, targets)
val old = strategyStates[index]
@@ -135,7 +141,9 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
status = StrategyTestingStatus.Completed
)
} finally {
stopService {}
stopService { error ->
_errorFlow.value = error
}
disableStrategy(current.path, prefs)
}
}
@@ -189,4 +197,7 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
fun clearVpnPermissionRequest() {
_requestVpnPermission.value = false
}
fun clearError() {
_errorFlow.value = ""
}
}

View File

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