25 Commits
2_0 ... 2_2

Author SHA1 Message Date
egor-white
c8c3b6de96 change ver 2025-07-07 12:25:46 +03:00
egor-white
b832be9556 add icon to status card 2025-07-07 12:22:38 +03:00
egor-white
817f3a0e7d move setupProxy checks to HomeViewModel.kt 2025-07-07 11:18:20 +03:00
egor-white
7d25f7e600 fix startVpn crashes 2025-07-06 20:48:35 +03:00
egor-white
8dcb4d1997 fix startVpn crashes 2025-07-06 12:34:39 +03:00
egor-white
ba2cf523bd Merge remote-tracking branch 'origin/main' 2025-07-05 14:35:51 +03:00
egor-white
c268ee8ccd get install unknown apps permission before loading, add module installation check on up startup 2025-07-05 14:30:23 +03:00
egor-white
72905f4180 change navbar icons 2025-07-05 13:19:38 +03:00
egor-white
a7bb250a64 move utils to com.cherret.zaprett.utils 2025-07-05 12:57:44 +03:00
CherretGit
cf15ed7559 Update workflow.yml 2025-07-04 18:38:21 +07:00
egor-white
77f6135f6a update ByeDpiVpnService.kt 2025-07-04 14:27:20 +03:00
egor-white
1997bb8403 Merge remote-tracking branch 'origin/main' 2025-07-04 14:17:03 +03:00
CherretGit
b266976a08 Update workflow.yml 2025-07-04 18:06:13 +07:00
egor-white
b5d9ec6f19 remove unused imports 2025-07-04 14:05:48 +03:00
egor-white
b0a4c3d2f5 move byedpi classes to package org.cherret.zaprett.byedpi 2025-07-04 14:04:48 +03:00
egor-white
8f2e7030d8 Update update.json 2025-07-04 12:23:25 +03:00
egor-white
aeb1c6123b Update changelog.md 2025-07-04 12:22:57 +03:00
egor-white
5a423f39c0 Update workflow.yml 2025-07-04 12:08:32 +03:00
egor-white
efeab387af change ver 2025-07-04 11:54:07 +03:00
egor-white
747a4b2052 bug fix 2025-07-04 11:53:16 +03:00
egor-white
6c6eeefcf1 add icon and module info card 2025-07-03 21:00:45 +03:00
CherretGit
0983262919 Update README.md 2025-06-30 12:31:03 +07:00
CherretGit
078c0085bb Update README.md 2025-06-30 12:30:13 +07:00
CherretGit
e87a0434de Update update.json 2025-06-30 01:16:34 +07:00
CherretGit
57d56cb5f2 Update changelog.md 2025-06-30 01:15:29 +07:00
35 changed files with 232 additions and 131 deletions

View File

@@ -28,6 +28,9 @@ jobs:
distribution: 'temurin'
cache: gradle
- name: Setup Git submodules
run: git submodule update --init --recursive
- name: Grant execute permission for gradlew
run: chmod +x gradlew

6
.gitmodules vendored Normal file
View File

@@ -0,0 +1,6 @@
[submodule "app/src/main/cpp/byedpi"]
path = app/src/main/cpp/byedpi
url = https://github.com/hufrea/byedpi
[submodule "app/src/main/jni/hev-socks5-tunnel"]
path = app/src/main/jni/hev-socks5-tunnel
url = https://github.com/heiher/hev-socks5-tunnel

View File

@@ -4,10 +4,10 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-03-24T08:57:47.584581613Z">
<DropdownSelection timestamp="2025-06-22T07:39:53.690794081Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=/home/dimap/.var/app/com.google.AndroidStudio/config/.android/avd/Medium_Phone.avd" />
<DeviceId pluginId="Default" identifier="serial=10.0.0.189:40463;connection=ffec9c3f" />
</handle>
</Target>
</DropdownSelection>

2
.idea/misc.xml generated
View File

@@ -1,6 +1,6 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@@ -2,9 +2,9 @@
## О приложении
Приложение разработано для работы с модулем [zaprett](https://github.com/egor-white/zaprett)
### [Официальный Telegram-канал приложения](https://t.me/zaprett_module)
## ВНИМАНИЕ приложение работает только с root правами
## ВНИМАНИЕ приложению желательно наличие root прав на устройстве, но есть режим работы без них на основе byedpi
На данный момент приложение умеет:
* Запускать, останавливать и перезапускать модуль
* Запускать, останавливать и перезапускать сервис
* Работа с листами (добавление, включение и выключение, загрузка из репозитория)
* Работа с стратегиями (добавление, выбор, загрузка из репозитория)
* Авто обновление приложения

View File

@@ -15,8 +15,8 @@ android {
applicationId = "com.cherret.zaprett"
minSdk = 30
targetSdk = 35
versionCode = 12
versionName = "2.0"
versionCode = 14
versionName = "2.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
ndk {

View File

@@ -20,8 +20,8 @@
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep class com.cherret.zaprett.TProxyService {
-keep class com.cherret.zaprett.byedpi.TProxyService {
native long[] TProxyGetStats();
native void TProxyStartService(java.lang.String, int);
native void TProxyStopService();
}
}

View File

@@ -45,7 +45,7 @@
</intent-filter>
</activity>
<service
android:name=".QSTileService"
android:name=".utils.QSTileService"
android:exported="true"
android:label="@string/qs_name"
android:icon="@drawable/ic_launcher_monochrome"
@@ -55,7 +55,7 @@
</intent-filter>
</service>
<service
android:name=".ByeDpiVpnService"
android:name=".byedpi.ByeDpiVpnService"
android:exported="true"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="specialUse">

View File

@@ -18,7 +18,7 @@ JNIEXPORT jint JNI_OnLoad(
}
JNIEXPORT jint JNICALL
Java_com_cherret_zaprett_NativeBridge_jniCreateSocket(
Java_com_cherret_zaprett_byedpi_NativeBridge_jniCreateSocket(
JNIEnv *env,
__attribute__((unused)) jobject thiz,
jobjectArray args) {
@@ -61,7 +61,7 @@ Java_com_cherret_zaprett_NativeBridge_jniCreateSocket(
}
JNIEXPORT jint JNICALL
Java_com_cherret_zaprett_NativeBridge_jniStartProxy(
Java_com_cherret_zaprett_byedpi_NativeBridge_jniStartProxy(
__attribute__((unused)) JNIEnv *env,
__attribute__((unused)) jobject thiz) {
@@ -76,7 +76,7 @@ Java_com_cherret_zaprett_NativeBridge_jniStartProxy(
}
JNIEXPORT jint JNICALL
Java_com_cherret_zaprett_NativeBridge_jniStopProxy(
Java_com_cherret_zaprett_byedpi_NativeBridge_jniStopProxy(
__attribute__((unused)) JNIEnv *env,
__attribute__((unused)) jobject thiz) {

View File

@@ -2,6 +2,7 @@ package com.cherret.zaprett
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
@@ -18,9 +19,9 @@ import androidx.activity.viewModels
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Dashboard
import androidx.compose.material.icons.filled.Dns
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.material3.AlertDialog
import androidx.compose.material3.Icon
@@ -30,13 +31,13 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
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.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.core.content.ContextCompat
import androidx.core.content.edit
@@ -55,14 +56,15 @@ import com.cherret.zaprett.ui.theme.ZaprettTheme
import com.cherret.zaprett.ui.viewmodel.HomeViewModel
import com.cherret.zaprett.ui.viewmodel.HostRepoViewModel
import com.cherret.zaprett.ui.viewmodel.StrategyRepoViewModel
import com.cherret.zaprett.utils.checkModuleInstallation
import com.google.firebase.Firebase
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.analytics
sealed class Screen(val route: String, @StringRes val nameResId: Int, val icon: ImageVector) {
object home : Screen("home", R.string.title_home, Icons.Default.Home)
object hosts : Screen("hosts", R.string.title_hosts, Icons.Default.Dashboard)
object strategies : Screen("strategies", R.string.title_strategies, Icons.Default.Dns)
object hosts : Screen("hosts", R.string.title_hosts, Icons.Default.Lan)
object strategies : Screen("strategies", R.string.title_strategies, Icons.Default.MultipleStop)
object settings : Screen("settings", R.string.title_settings, Icons.Default.Settings)
}
val topLevelRoutes = listOf(Screen.home, Screen.hosts, Screen.strategies, Screen.settings)
@@ -86,6 +88,17 @@ class MainActivity : ComponentActivity() {
setContent {
ZaprettTheme {
val sharedPreferences = remember { getSharedPreferences("settings", MODE_PRIVATE) }
LaunchedEffect(Unit) {
checkModuleInstallation { result ->
if (getSharedPreferences("settings", Context.MODE_PRIVATE).getBoolean("use_module", false) && !result) sharedPreferences.edit {
putBoolean(
"use_module",
false
)
}
}
}
var showStoragePermissionDialog by remember { mutableStateOf(!Environment.isExternalStorageManager()) }
var showNotificationPermissionDialog by remember {
mutableStateOf(
@@ -133,6 +146,7 @@ class MainActivity : ComponentActivity() {
}
}
@Composable
fun BottomBar() {
val navController = rememberNavController()

View File

@@ -1,11 +1,10 @@
package com.cherret.zaprett
package com.cherret.zaprett.byedpi
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.net.VpnService
@@ -13,6 +12,9 @@ import android.os.ParcelFileDescriptor
import android.util.Log
import android.widget.Toast
import androidx.core.app.NotificationCompat
import com.cherret.zaprett.MainActivity
import com.cherret.zaprett.R
import com.cherret.zaprett.utils.getActiveStrategy
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -30,7 +32,7 @@ class ByeDpiVpnService : VpnService() {
@SuppressLint("ForegroundServiceType")
override fun onCreate() {
super.onCreate()
sharedPreferences = getSharedPreferences("settings", Context.MODE_PRIVATE)
sharedPreferences = getSharedPreferences("settings", MODE_PRIVATE)
createNotificationChannel()
}
@@ -38,6 +40,7 @@ class ByeDpiVpnService : VpnService() {
super.onStartCommand(intent, flags, startId)
return when (intent?.action) {
"START_VPN" -> {
startForeground(NOTIFICATION_ID, createNotification())
setupProxy()
START_STICKY
}
@@ -96,23 +99,14 @@ class ByeDpiVpnService : VpnService() {
}
private fun setupProxy() {
if (getActiveStrategy(sharedPreferences).isNotEmpty()) {
startForeground(NOTIFICATION_ID, createNotification())
try {
startSocksProxy()
startByeDpi()
status = ServiceStatus.Connected
} catch (e: Exception) {
Log.e("proxy", "Failed to start")
status = ServiceStatus.Failed
}
}
else {
Toast.makeText(
this@ByeDpiVpnService,
getString(R.string.toast_no_strategy_selected),
Toast.LENGTH_SHORT
).show()
try {
startSocksProxy()
startByeDpi()
status = ServiceStatus.Connected
} catch (e: Exception) {
Log.e("proxy", "Failed to start")
status = ServiceStatus.Failed
stopSelf()
}
}
@@ -155,6 +149,7 @@ class ByeDpiVpnService : VpnService() {
private fun stopProxy() {
try {
vpnInterface?.close()
vpnInterface = null
NativeBridge().stopProxy()
TProxyService.TProxyStopService()
status = ServiceStatus.Disconnected

View File

@@ -1,4 +1,4 @@
package com.cherret.zaprett
package com.cherret.zaprett.byedpi
class NativeBridge {
companion object {

View File

@@ -1,4 +1,4 @@
package com.cherret.zaprett
package com.cherret.zaprett.byedpi
enum class ServiceStatus {
Disconnected,

View File

@@ -1,4 +1,4 @@
package com.cherret.zaprett
package com.cherret.zaprett.byedpi
object TProxyService {
init {

View File

@@ -10,14 +10,18 @@ import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.Dangerous
@@ -47,6 +51,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
@@ -73,6 +78,7 @@ fun HomeScreen(viewModel: HomeViewModel = viewModel(), vpnLauncher: ActivityResu
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
val cardText = viewModel.cardText
val cardIcon = viewModel.cardIcon;
val changeLog = viewModel.changeLog
val newVersion = viewModel.newVersion
val updateAvailable = viewModel.updateAvailable
@@ -114,8 +120,10 @@ fun HomeScreen(viewModel: HomeViewModel = viewModel(), vpnLauncher: ActivityResu
},
snackbarHost = { SnackbarHost(snackbarHostState) },
content = { paddingValues ->
Column(modifier = Modifier.padding(paddingValues)) {
ServiceStatusCard(viewModel, cardText, snackbarHostState, scope)
Column(modifier = Modifier
.padding(paddingValues)
.verticalScroll(rememberScrollState())) {
ServiceStatusCard(viewModel, cardText, cardIcon, snackbarHostState, scope)
UpdateCard(updateAvailable) { viewModel.showUpdateDialog() }
if (showUpdateDialog) {
UpdateDialog(viewModel, changeLog.value.orEmpty(), newVersion) { viewModel.dismissUpdateDialog() }
@@ -133,25 +141,40 @@ fun HomeScreen(viewModel: HomeViewModel = viewModel(), vpnLauncher: ActivityResu
}
@Composable
private fun ServiceStatusCard(viewModel: HomeViewModel, cardText: MutableState<Int>, snackbarHostState: SnackbarHostState, scope: CoroutineScope) {
private fun ServiceStatusCard(viewModel: HomeViewModel, cardText: MutableState<Int>, cardIcon : MutableState<ImageVector>, snackbarHostState: SnackbarHostState, scope: CoroutineScope) {
ElevatedCard(
elevation = CardDefaults.cardElevation(6.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primary),
modifier = Modifier
.fillMaxWidth()
.padding(start = 10.dp, top = 25.dp, end = 10.dp)
.width(240.dp)
.height(150.dp),
onClick = { viewModel.onCardClick() }
) {
Text(
text = stringResource(cardText.value),
fontFamily = FontFamily(Font(R.font.unbounded, FontWeight.Normal)),
Row (
modifier = Modifier
.fillMaxWidth()
.fillMaxSize()
.padding(16.dp),
textAlign = TextAlign.Center
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
)
{
Icon(
painter = rememberVectorPainter(cardIcon.value),
modifier = Modifier
.width(60.dp)
.height(60.dp),
contentDescription = "icon"
)
Text(
text = stringResource(cardText.value),
fontFamily = FontFamily(Font(R.font.unbounded, FontWeight.Normal)),
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
textAlign = TextAlign.Center
)
}
}
}

View File

@@ -22,13 +22,14 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.cherret.zaprett.BuildConfig
import com.cherret.zaprett.ByeDpiVpnService
import com.cherret.zaprett.byedpi.ByeDpiVpnService
import com.cherret.zaprett.R
import com.cherret.zaprett.ServiceStatus
import com.cherret.zaprett.checkModuleInstallation
import com.cherret.zaprett.checkRoot
import com.cherret.zaprett.getStartOnBoot
import com.cherret.zaprett.setStartOnBoot
import com.cherret.zaprett.byedpi.ServiceStatus
import com.cherret.zaprett.utils.checkModuleInstallation
import com.cherret.zaprett.utils.checkRoot
import com.cherret.zaprett.utils.getStartOnBoot
import com.cherret.zaprett.utils.setStartOnBoot
import com.cherret.zaprett.utils.stopService
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -66,7 +67,10 @@ fun SettingsScreen() {
openNoRootDialog = openNoRootDialog,
openNoModuleDialog = openNoModuleDialog
) { success ->
if (success) useModule.value = isChecked
if (success) {
useModule.value = isChecked
if (!isChecked) stopService { }
}
}
}
),

View File

@@ -13,8 +13,8 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.AndroidViewModel
import com.cherret.zaprett.R
import com.cherret.zaprett.getZaprettPath
import com.cherret.zaprett.restartService
import com.cherret.zaprett.utils.getZaprettPath
import com.cherret.zaprett.utils.restartService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import java.io.File

View File

@@ -9,13 +9,13 @@ import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.cherret.zaprett.RepoItemInfo
import com.cherret.zaprett.utils.RepoItemInfo
import com.cherret.zaprett.R
import com.cherret.zaprett.download
import com.cherret.zaprett.getFileSha256
import com.cherret.zaprett.getZaprettPath
import com.cherret.zaprett.registerDownloadListenerHost
import com.cherret.zaprett.restartService
import com.cherret.zaprett.utils.download
import com.cherret.zaprett.utils.getFileSha256
import com.cherret.zaprett.utils.getZaprettPath
import com.cherret.zaprett.utils.registerDownloadListenerHost
import com.cherret.zaprett.utils.restartService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File

View File

@@ -3,25 +3,32 @@ package com.cherret.zaprett.ui.viewmodel
import android.app.Application
import android.content.Context
import android.content.Intent
import android.widget.Toast
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Help
import androidx.compose.material.icons.filled.Cancel
import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material3.Icon
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.core.content.ContextCompat
import androidx.lifecycle.AndroidViewModel
import com.cherret.zaprett.ByeDpiVpnService
import com.cherret.zaprett.byedpi.ByeDpiVpnService
import com.cherret.zaprett.R
import com.cherret.zaprett.ServiceStatus
import com.cherret.zaprett.download
import com.cherret.zaprett.getBinVersion
import com.cherret.zaprett.getChangelog
import com.cherret.zaprett.getModuleVersion
import com.cherret.zaprett.getStatus
import com.cherret.zaprett.getUpdate
import com.cherret.zaprett.installApk
import com.cherret.zaprett.registerDownloadListener
import com.cherret.zaprett.restartService
import com.cherret.zaprett.startService
import com.cherret.zaprett.stopService
import com.cherret.zaprett.byedpi.ServiceStatus
import com.cherret.zaprett.utils.download
import com.cherret.zaprett.utils.getActiveStrategy
import com.cherret.zaprett.utils.getBinVersion
import com.cherret.zaprett.utils.getChangelog
import com.cherret.zaprett.utils.getModuleVersion
import com.cherret.zaprett.utils.getStatus
import com.cherret.zaprett.utils.getUpdate
import com.cherret.zaprett.utils.installApk
import com.cherret.zaprett.utils.registerDownloadListener
import com.cherret.zaprett.utils.restartService
import com.cherret.zaprett.utils.startService
import com.cherret.zaprett.utils.stopService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -34,6 +41,8 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
val requestVpnPermission = _requestVpnPermission.asStateFlow()
var cardText = mutableIntStateOf(R.string.status_not_availible) // MVP temporarily(maybe)
private set
var cardIcon = mutableStateOf(Icons.AutoMirrored.Filled.Help)
private set
var moduleVer = mutableStateOf(context.getString(R.string.unknown_text))
private set
@@ -41,7 +50,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
var nfqwsVer = mutableStateOf(context.getString(R.string.unknown_text))
private set
var byedpiVer = mutableStateOf(context.getString(R.string.unknown_text))
var byedpiVer = mutableStateOf("0.17.1")
private set
var serviceMode = mutableIntStateOf(R.string.service_mode_ciadpi)
@@ -77,21 +86,49 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
fun checkServiceStatus() {
if (prefs.getBoolean("use_module", false) && prefs.getBoolean("update_on_boot", false)) {
getStatus { isEnabled ->
cardText.intValue = if (isEnabled) R.string.status_enabled else R.string.status_disabled
if (isEnabled){
cardText.value = R.string.status_enabled
cardIcon.value = Icons.Filled.CheckCircle
}
else {
cardText.value = R.string.status_disabled
cardIcon.value = Icons.Filled.Cancel
}
}
}
else {
cardText.value = if (ByeDpiVpnService.status == ServiceStatus.Connected) R.string.status_enabled else R.string.status_disabled
if (ByeDpiVpnService.status == ServiceStatus.Connected){
cardText.value = R.string.status_enabled
cardIcon.value = Icons.Filled.CheckCircle
}
else {
cardText.value = R.string.status_disabled
cardIcon.value = Icons.Filled.Cancel
}
}
}
fun onCardClick() {
if (prefs.getBoolean("use_module", false)) {
getStatus { isEnabled ->
cardText.value = if (isEnabled) R.string.status_enabled else R.string.status_disabled
if (isEnabled){
cardText.value = R.string.status_enabled
cardIcon.value = Icons.Filled.CheckCircle
}
else {
cardText.value = R.string.status_disabled
cardIcon.value = Icons.Filled.Cancel
}
}
} else {
cardText.value = if (ByeDpiVpnService.status == ServiceStatus.Connected) R.string.status_enabled else R.string.status_disabled
if (ByeDpiVpnService.status == ServiceStatus.Connected){
cardText.value = R.string.status_enabled
cardIcon.value = Icons.Filled.CheckCircle
}
else {
cardText.value = R.string.status_disabled
cardIcon.value = Icons.Filled.Cancel
}
}
}
@@ -113,10 +150,19 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
}
} else {
if (ByeDpiVpnService.status == ServiceStatus.Disconnected || ByeDpiVpnService.status == ServiceStatus.Failed) {
scope.launch {
snackbarHostState.showSnackbar(context.getString(R.string.snack_starting_service))
if (getActiveStrategy(prefs).isNotEmpty()) {
scope.launch {
snackbarHostState.showSnackbar(context.getString(R.string.snack_starting_service))
}
_requestVpnPermission.value = true
}
else {
Toast.makeText(
context,
context.getString(R.string.toast_no_strategy_selected),
Toast.LENGTH_SHORT
).show()
}
_requestVpnPermission.value = true
}
else {
scope.launch {
@@ -201,4 +247,12 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
installApk(context, uri)
}
}
fun parseArgs(ip: String, port: String, lines: List<String>): Array<String> {
val regex = Regex("""--?\S+(?:=(?:[^"'\s]+|"[^"]*"|'[^']*'))?|[^\s]+""")
val parsedArgs = lines
.flatMap { line -> regex.findAll(line).map { it.value } }
return arrayOf("ciadpi", "--ip", ip, "--port", port) + parsedArgs
}
}

View File

@@ -1,16 +1,9 @@
package com.cherret.zaprett.ui.viewmodel
import android.app.Application
import androidx.lifecycle.viewModelScope
import com.cherret.zaprett.RepoItemInfo
import com.cherret.zaprett.download
import com.cherret.zaprett.getAllLists
import com.cherret.zaprett.getHostList
import com.cherret.zaprett.getZaprettPath
import com.cherret.zaprett.registerDownloadListenerHost
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
import com.cherret.zaprett.utils.RepoItemInfo
import com.cherret.zaprett.utils.getAllLists
import com.cherret.zaprett.utils.getHostList
class HostRepoViewModel(application: Application): BaseRepoViewModel(application) {
override fun getInstalledLists(): Array<String> = getAllLists()

View File

@@ -3,11 +3,11 @@ package com.cherret.zaprett.ui.viewmodel
import android.app.Application
import android.content.Context
import androidx.compose.material3.SnackbarHostState
import com.cherret.zaprett.disableList
import com.cherret.zaprett.enableList
import com.cherret.zaprett.getActiveLists
import com.cherret.zaprett.getAllLists
import com.cherret.zaprett.getStatus
import com.cherret.zaprett.utils.disableList
import com.cherret.zaprett.utils.enableList
import com.cherret.zaprett.utils.getActiveLists
import com.cherret.zaprett.utils.getAllLists
import com.cherret.zaprett.utils.getStatus
import kotlinx.coroutines.CoroutineScope
import java.io.File

View File

@@ -1,10 +1,10 @@
package com.cherret.zaprett.ui.viewmodel
import android.app.Application
import com.cherret.zaprett.RepoItemInfo
import com.cherret.zaprett.getAllByeDPIStrategies
import com.cherret.zaprett.getAllNfqwsStrategies
import com.cherret.zaprett.getStrategiesList
import com.cherret.zaprett.utils.RepoItemInfo
import com.cherret.zaprett.utils.getAllByeDPIStrategies
import com.cherret.zaprett.utils.getAllNfqwsStrategies
import com.cherret.zaprett.utils.getStrategiesList
class StrategyRepoViewModel(application: Application): BaseRepoViewModel(application) {
override fun getInstalledLists(): Array<String> =

View File

@@ -4,15 +4,15 @@ import android.app.Application
import android.content.Context
import android.content.SharedPreferences
import androidx.compose.material3.SnackbarHostState
import com.cherret.zaprett.ByeDpiVpnService
import com.cherret.zaprett.ServiceStatus
import com.cherret.zaprett.disableStrategy
import com.cherret.zaprett.enableStrategy
import com.cherret.zaprett.getActiveByeDPIStrategies
import com.cherret.zaprett.getActiveNfqwsStrategies
import com.cherret.zaprett.getAllByeDPIStrategies
import com.cherret.zaprett.getAllNfqwsStrategies
import com.cherret.zaprett.getStatus
import com.cherret.zaprett.byedpi.ByeDpiVpnService
import com.cherret.zaprett.byedpi.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.getAllByeDPIStrategies
import com.cherret.zaprett.utils.getAllNfqwsStrategies
import com.cherret.zaprett.utils.getStatus
import kotlinx.coroutines.CoroutineScope
import java.io.File

View File

@@ -1,4 +1,4 @@
package com.cherret.zaprett
package com.cherret.zaprett.utils
import android.content.SharedPreferences
import android.os.Environment
@@ -200,7 +200,7 @@ fun getActiveStrategy(sharedPreferences: SharedPreferences): List<String> {
}
fun enableList(path: String, sharedPreferences: SharedPreferences) {
if (sharedPreferences.getBoolean("use-module", false)) {
if (sharedPreferences.getBoolean("use_module", false)) {
val props = Properties()
val configFile = getConfigFile()
try {
@@ -264,7 +264,7 @@ fun enableStrategy(path: String, sharedPreferences: SharedPreferences) {
}
fun disableList(path: String, sharedPreferences: SharedPreferences) {
if (sharedPreferences.getBoolean("use-module", false)) {
if (sharedPreferences.getBoolean("use_module", false)) {
val props = Properties()
val configFile = getConfigFile()
try {
@@ -328,4 +328,4 @@ fun disableStrategy(path: String, sharedPreferences: SharedPreferences) {
else {
sharedPreferences.edit { remove("active_strategy") }
}
}
}

View File

@@ -1,7 +1,8 @@
package com.cherret.zaprett
package com.cherret.zaprett.utils
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
import com.cherret.zaprett.R
class QSTileService: TileService() {
override fun onTileAdded() {

View File

@@ -1,4 +1,4 @@
package com.cherret.zaprett
package com.cherret.zaprett.utils
import android.annotation.SuppressLint
import android.app.DownloadManager

View File

@@ -1,4 +1,4 @@
package com.cherret.zaprett
package com.cherret.zaprett.utils
import android.annotation.SuppressLint
import android.app.DownloadManager
@@ -13,6 +13,7 @@ import android.provider.Settings
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.core.net.toUri
import com.cherret.zaprett.BuildConfig
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import okhttp3.Call
@@ -101,6 +102,11 @@ fun installApk(context: Context, uri: Uri) {
}
fun registerDownloadListener(context: Context, downloadId: Long, onDownloaded: (Uri) -> Unit) {// AI Generated
if (!context.packageManager.canRequestPackageInstalls()){
val packageUri = Uri.fromParts("package", context.packageName, null)
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageUri)
context.startActivity(intent)
}
val receiver = object : BroadcastReceiver() {
@SuppressLint("Range")
override fun onReceive(context: Context?, intent: Intent?) {

View File

@@ -16,6 +16,6 @@
APP_OPTIM := release
APP_PLATFORM := android-21
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
APP_CFLAGS := -O3 -DPKGNAME=com/cherret/zaprett
APP_CFLAGS := -O3 -DPKGNAME=com/cherret/zaprett/byedpi
APP_CPPFLAGS := -O3 -std=c++11
NDK_TOOLCHAIN_VERSION := clang

View File

@@ -72,7 +72,7 @@
<string name="qs_stopping">Остановка…</string>
<string name="module_version">Версия модуля</string>
<string name="nfqws_version">Версия nfqws</string>
<string name="ciadpi_version">Версия ciadpi 0.17.1</string>
<string name="ciadpi_version">Версия ciadpi</string>
<string name="unknown_text">неизвестно</string>
<string name="service_mode">Режим работы</string>
<string name="qs_not_available">Не доступно</string>

View File

@@ -73,7 +73,7 @@
<string name="qs_stopping">Stopping…</string>
<string name="module_version">Module version</string>
<string name="nfqws_version">nfqws version</string>
<string name="ciadpi_version">ciadpi version 0.17.1</string>
<string name="ciadpi_version">ciadpi version</string>
<string name="unknown_text">unknown</string>
<string name="service_mode">Service mode</string>
<string name="service_mode_nfqws" translatable="false">nfqws</string>

View File

@@ -1 +1,3 @@
Добавлена вкладка "Стратегии"
Исправление багов в фрагменте хостов
Исправление сплющивания карточки информации о модуле
Перенос версии byedpi в соответствующее место

View File

@@ -25,7 +25,6 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-lifecycle-service = { group = "androidx.lifecycle", name = "lifecycle-service", version.ref = "lifecycleService" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }

View File

@@ -1,6 +1,6 @@
{
"version": "1.10",
"versionCode": 11,
"downloadUrl": "https://github.com/CherretGit/zaprett-app/releases/download/1_10_0/app-release.apk",
"version": "2.1",
"versionCode": 13,
"downloadUrl": "https://github.com/CherretGit/zaprett-app/releases/download/2_1/app-release.apk",
"changelogUrl": "https://raw.githubusercontent.com/CherretGit/zaprett-app/refs/heads/main/changelog.md"
}