mirror of
https://github.com/CherretGit/zaprett-app.git
synced 2025-12-10 05:29:37 +05:00
add module error handling (and showing) in HostsScreen.kt, IpsetsScreen.kt, StrategyScreen.kt, StrategySelectionScreen.kt
This commit is contained in:
@@ -1,7 +1,11 @@
|
|||||||
package com.cherret.zaprett.ui.screen
|
package com.cherret.zaprett.ui.screen
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.os.Build
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
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)
|
if (getHostListMode(prefs) == "whitelist") viewModel.copySelectedFile(context, "/lists/include", it)
|
||||||
else viewModel.copySelectedFile(context, "/lists/exclude", it) }
|
else viewModel.copySelectedFile(context, "/lists/exclude", it) }
|
||||||
}
|
}
|
||||||
|
val error by viewModel.errorFlow.collectAsState()
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.refresh()
|
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(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package com.cherret.zaprett.ui.screen
|
package com.cherret.zaprett.ui.screen
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.os.Build
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
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)
|
if (getHostListMode(prefs) == "whitelist") viewModel.copySelectedFile(context, "/ipset/include", it)
|
||||||
else viewModel.copySelectedFile(context, "/ipset/exclude", it) }
|
else viewModel.copySelectedFile(context, "/ipset/exclude", it) }
|
||||||
}
|
}
|
||||||
|
val error by viewModel.errorFlow.collectAsState()
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.refresh()
|
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(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package com.cherret.zaprett.ui.screen
|
package com.cherret.zaprett.ui.screen
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
@@ -73,11 +77,43 @@ fun StrategyScreen(navController: NavController, viewModel: StrategyViewModel =
|
|||||||
it
|
it
|
||||||
) }
|
) }
|
||||||
}
|
}
|
||||||
|
val error by viewModel.errorFlow.collectAsState()
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.refresh()
|
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(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
package com.cherret.zaprett.ui.screen
|
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.Context.MODE_PRIVATE
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
|
import android.os.Build
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -59,6 +64,7 @@ fun StrategySelectionScreen(navController: NavController, vpnLauncher: ActivityR
|
|||||||
val prefs = context.getSharedPreferences("settings", MODE_PRIVATE)
|
val prefs = context.getSharedPreferences("settings", MODE_PRIVATE)
|
||||||
var showDialog = remember { mutableStateOf(false) }
|
var showDialog = remember { mutableStateOf(false) }
|
||||||
val requestVpnPermission by viewModel.requestVpnPermission.collectAsState()
|
val requestVpnPermission by viewModel.requestVpnPermission.collectAsState()
|
||||||
|
val error by viewModel.errorFlow.collectAsState()
|
||||||
|
|
||||||
if (showDialog.value) {
|
if (showDialog.value) {
|
||||||
InfoAlert { showDialog.value = false }
|
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(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.cherret.zaprett.utils.restartService
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
@@ -37,6 +38,9 @@ abstract class BaseListsViewModel(application: Application) : AndroidViewModel(a
|
|||||||
var isRefreshing by mutableStateOf(false)
|
var isRefreshing by mutableStateOf(false)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
private val _errorFlow = MutableStateFlow("")
|
||||||
|
val errorFlow = _errorFlow.asStateFlow()
|
||||||
|
|
||||||
private var _showNoPermissionDialog = MutableStateFlow(false)
|
private var _showNoPermissionDialog = MutableStateFlow(false)
|
||||||
val showNoPermissionDialog: StateFlow<Boolean> = _showNoPermissionDialog
|
val showNoPermissionDialog: StateFlow<Boolean> = _showNoPermissionDialog
|
||||||
|
|
||||||
@@ -72,12 +76,18 @@ abstract class BaseListsViewModel(application: Application) : AndroidViewModel(a
|
|||||||
actionLabel = context.getString(R.string.btn_restart_service)
|
actionLabel = context.getString(R.string.btn_restart_service)
|
||||||
)
|
)
|
||||||
if (result == SnackbarResult.ActionPerformed) {
|
if (result == SnackbarResult.ActionPerformed) {
|
||||||
restartService {}
|
restartService { error ->
|
||||||
|
_errorFlow.value = error
|
||||||
|
}
|
||||||
snackbarHostState.showSnackbar(context.getString(R.string.snack_reload))
|
snackbarHostState.showSnackbar(context.getString(R.string.snack_reload))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clearError() {
|
||||||
|
_errorFlow.value = ""
|
||||||
|
}
|
||||||
|
|
||||||
fun copySelectedFile(context: Context, path: String, uri: Uri) {
|
fun copySelectedFile(context: Context, path: String, uri: Uri) {
|
||||||
//if (!Environment.isExternalStorageManager()) return
|
//if (!Environment.isExternalStorageManager()) return
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
|
|||||||
val context = application
|
val context = application
|
||||||
private val _requestVpnPermission = MutableStateFlow(false)
|
private val _requestVpnPermission = MutableStateFlow(false)
|
||||||
val requestVpnPermission = _requestVpnPermission.asStateFlow()
|
val requestVpnPermission = _requestVpnPermission.asStateFlow()
|
||||||
|
private val _errorFlow = MutableStateFlow("")
|
||||||
|
val errorFlow = _errorFlow.asStateFlow()
|
||||||
val strategyStates = mutableStateListOf<StrategyCheckResult>()
|
val strategyStates = mutableStateListOf<StrategyCheckResult>()
|
||||||
var noHostsCard = mutableStateOf(false)
|
var noHostsCard = mutableStateOf(false)
|
||||||
private set
|
private set
|
||||||
@@ -125,8 +127,12 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
|
|||||||
strategyStates[index] = current.copy(status = StrategyTestingStatus.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 { error ->
|
||||||
startService {}
|
_errorFlow.value = error
|
||||||
|
} }
|
||||||
|
startService { error ->
|
||||||
|
_errorFlow.value = error
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
val progress = countReachable(index, targets)
|
val progress = countReachable(index, targets)
|
||||||
val old = strategyStates[index]
|
val old = strategyStates[index]
|
||||||
@@ -135,7 +141,9 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
|
|||||||
status = StrategyTestingStatus.Completed
|
status = StrategyTestingStatus.Completed
|
||||||
)
|
)
|
||||||
} finally {
|
} finally {
|
||||||
stopService {}
|
stopService { error ->
|
||||||
|
_errorFlow.value = error
|
||||||
|
}
|
||||||
disableStrategy(current.path, prefs)
|
disableStrategy(current.path, prefs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,4 +197,7 @@ class StrategySelectionViewModel(application: Application) : AndroidViewModel(ap
|
|||||||
fun clearVpnPermissionRequest() {
|
fun clearVpnPermissionRequest() {
|
||||||
_requestVpnPermission.value = false
|
_requestVpnPermission.value = false
|
||||||
}
|
}
|
||||||
|
fun clearError() {
|
||||||
|
_errorFlow.value = ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user