added module errors handling on service start, stop, restart, added automated service status updating on start, stop, restart

This commit is contained in:
white
2025-12-04 16:08:05 +03:00
parent b5e3d256a6
commit 0065b8a92b
5 changed files with 102 additions and 14 deletions

View File

@@ -2,5 +2,26 @@
<project version="4"> <project version="4">
<component name="AppInsightsSettings"> <component name="AppInsightsSettings">
<option name="selectedTabId" value="Android Vitals" /> <option name="selectedTabId" value="Android Vitals" />
<option name="tabSettings">
<map>
<entry key="Firebase Crashlytics">
<value>
<InsightsFilterSettings>
<option name="connection">
<ConnectionSetting>
<option name="appId" value="com.cherret.zaprett" />
<option name="mobileSdkAppId" value="1:1005804036856:android:e7db5546b8bb4daf91510d" />
<option name="projectId" value="zaprett-app" />
<option name="projectNumber" value="1005804036856" />
</ConnectionSetting>
</option>
<option name="signal" value="SIGNAL_UNSPECIFIED" />
<option name="timeIntervalDays" value="THIRTY_DAYS" />
<option name="visibilityType" value="ALL" />
</InsightsFilterSettings>
</value>
</entry>
</map>
</option>
</component> </component>
</project> </project>

View File

@@ -209,7 +209,7 @@ class MainActivity : ComponentActivity() {
startDestination = Screen.home.route, startDestination = Screen.home.route,
Modifier.padding(innerPadding) Modifier.padding(innerPadding)
) { ) {
composable(Screen.home.route) { HomeScreen(viewModel = viewModel, vpnPermissionLauncher) } composable(Screen.home.route) { HomeScreen(viewModel = viewModel, navController,vpnPermissionLauncher) }
composable(Screen.hosts.route) { HostsScreen(navController) } composable(Screen.hosts.route) { HostsScreen(navController) }
composable(Screen.strategies.route) { StrategyScreen(navController) } composable(Screen.strategies.route) { StrategyScreen(navController) }
composable(Screen.ipsets.route) { IpsetsScreen(navController) } composable(Screen.ipsets.route) { IpsetsScreen(navController) }

View File

@@ -1,9 +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
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
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.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically import androidx.compose.animation.expandVertically
@@ -65,16 +69,19 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.cherret.zaprett.BuildConfig import com.cherret.zaprett.BuildConfig
import com.cherret.zaprett.R import com.cherret.zaprett.R
import com.cherret.zaprett.data.ServiceStatusUI import com.cherret.zaprett.data.ServiceStatusUI
import com.cherret.zaprett.ui.viewmodel.HomeViewModel import com.cherret.zaprett.ui.viewmodel.HomeViewModel
import dev.jeziellago.compose.markdowntext.MarkdownText import dev.jeziellago.compose.markdowntext.MarkdownText
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.serialization.SerializationException
import java.io.IOException
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun HomeScreen(viewModel: HomeViewModel = viewModel(), vpnLauncher: ActivityResultLauncher<Intent>) { fun HomeScreen(viewModel: HomeViewModel = viewModel(), navController: NavController, vpnLauncher: ActivityResultLauncher<Intent>) {
val context = LocalContext.current val context = LocalContext.current
val sharedPreferences: SharedPreferences = remember { context.getSharedPreferences("settings", Context.MODE_PRIVATE) } val sharedPreferences: SharedPreferences = remember { context.getSharedPreferences("settings", Context.MODE_PRIVATE) }
val requestVpnPermission by viewModel.requestVpnPermission.collectAsState() val requestVpnPermission by viewModel.requestVpnPermission.collectAsState()
@@ -89,6 +96,7 @@ fun HomeScreen(viewModel: HomeViewModel = viewModel(), vpnLauncher: ActivityResu
val nfqwsVer = viewModel.nfqwsVer; val nfqwsVer = viewModel.nfqwsVer;
val byedpiVer = viewModel.byedpiVer; val byedpiVer = viewModel.byedpiVer;
val serviceMode = viewModel.serviceMode val serviceMode = viewModel.serviceMode
val error by viewModel.errorFlow.collectAsState()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
viewModel.checkForUpdate() viewModel.checkForUpdate()
viewModel.checkServiceStatus() viewModel.checkServiceStatus()
@@ -107,6 +115,39 @@ fun HomeScreen(viewModel: HomeViewModel = viewModel(), vpnLauncher: ActivityResu
} }
} }
if (error.isNotEmpty()) {
AlertDialog(
onDismissRequest = {
viewModel.clearError()
navController.popBackStack()
},
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()
navController.popBackStack()
}) {
Text(stringResource(R.string.btn_continue))
}
}
)
}
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(

View File

@@ -45,6 +45,9 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
private val _serviceStatus = MutableStateFlow(ServiceStatusUI()) private val _serviceStatus = MutableStateFlow(ServiceStatusUI())
val serviceStatus: StateFlow<ServiceStatusUI> = _serviceStatus.asStateFlow() val serviceStatus: StateFlow<ServiceStatusUI> = _serviceStatus.asStateFlow()
private val _errorFlow = MutableStateFlow("")
val errorFlow = _errorFlow.asStateFlow()
var moduleVer = mutableStateOf(context.getString(R.string.unknown_text)) var moduleVer = mutableStateOf(context.getString(R.string.unknown_text))
private set private set
@@ -129,7 +132,10 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
) )
) )
} }
if (!isEnabled) startService {} if (!isEnabled) startService { error ->
_errorFlow.value = error
onCardClick()
}
} }
} else { } else {
if (ByeDpiVpnService.status == ServiceStatus.Disconnected || ByeDpiVpnService.status == ServiceStatus.Failed) { if (ByeDpiVpnService.status == ServiceStatus.Disconnected || ByeDpiVpnService.status == ServiceStatus.Failed) {
@@ -169,7 +175,10 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
) )
) )
} }
if (isEnabled) stopService {} if (isEnabled) stopService { error ->
_errorFlow.value = error
onCardClick()
}
} }
} else { } else {
if (ByeDpiVpnService.status == ServiceStatus.Connected) { if (ByeDpiVpnService.status == ServiceStatus.Connected) {
@@ -192,7 +201,10 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
fun onBtnRestart(snackbarHostState: SnackbarHostState, scope: CoroutineScope) { fun onBtnRestart(snackbarHostState: SnackbarHostState, scope: CoroutineScope) {
if (prefs.getBoolean("use_module", false)) { if (prefs.getBoolean("use_module", false)) {
restartService {} restartService { error ->
_errorFlow.value = error
onCardClick()
}
scope.launch { scope.launch {
snackbarHostState.showSnackbar(context.getString(R.string.snack_reload)) snackbarHostState.showSnackbar(context.getString(R.string.snack_reload))
} }
@@ -242,6 +254,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
} }
} }
// unused?
fun parseArgs(ip: String, port: String, lines: List<String>): Array<String> { fun parseArgs(ip: String, port: String, lines: List<String>): Array<String> {
val regex = Regex("""--?\S+(?:=(?:[^"'\s]+|"[^"]*"|'[^']*'))?|[^\s]+""") val regex = Regex("""--?\S+(?:=(?:[^"'\s]+|"[^"]*"|'[^']*'))?|[^\s]+""")
val parsedArgs = lines val parsedArgs = lines
@@ -249,4 +262,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
return arrayOf("ciadpi", "--ip", ip, "--port", port) + parsedArgs return arrayOf("ciadpi", "--ip", ip, "--port", port) + parsedArgs
} }
fun clearError() {
_errorFlow.value = ""
}
} }

View File

@@ -75,21 +75,31 @@ fun getStatus(callback: (Boolean) -> Unit) {
} }
} }
fun startService(callback: (Boolean) -> Unit) { fun startService(callback: (String) -> Unit) {
Shell.cmd("zaprett start").submit { result -> Shell.cmd("zaprett start 2>&1").submit { result ->
callback(result.isSuccess) callback(
if (result.isSuccess) ""
else result.out.joinToString("\n")
)
} }
} }
fun stopService(callback: (Boolean) -> Unit) {
Shell.cmd("zaprett stop").submit { result -> fun stopService(callback: (String) -> Unit) {
callback(result.isSuccess) Shell.cmd("zaprett stop 2>&1").submit { result ->
callback(
if (result.isSuccess) ""
else result.out.joinToString("\n")
)
} }
} }
fun restartService(callback: (Boolean) -> Unit) { fun restartService(callback: (String) -> Unit) {
Shell.cmd("zaprett restart").submit { result -> Shell.cmd("zaprett restart 2>&1").submit { result ->
callback(result.isSuccess) callback(
if (result.isSuccess) ""
else result.out.joinToString("\n")
)
} }
} }