31 Commits
2_1 ... 2_3

Author SHA1 Message Date
egor-white
91e627ced6 fix startActivity() crashes 2025-07-08 19:44:10 +03:00
CherretGit
0819cbab73 Update update.json 2025-07-08 21:47:24 +07:00
egor-white
dddac71bc6 Merge remote-tracking branch 'origin/main' 2025-07-08 17:31:18 +03:00
egor-white
2d4850c0e8 add Updater.kt mediastore if sdk < 30 2025-07-08 17:31:00 +03:00
CherretGit
a466ad8bcd Update update.json 2025-07-08 21:11:58 +07:00
CherretGit
1121365dfb Update changelog.md 2025-07-08 21:10:10 +07:00
egor-white
212df7fa9b Android 10 Storage crashes fix 2025-07-08 16:48:46 +03:00
egor-white
5e01ebae7d Merge pull request #3 from CherretGit/revert-1-main
Revert "Android 10 fix & update libs"
2025-07-08 14:57:44 +03:00
egor-white
2e2b33ca55 Revert "Android 10 fix & update libs" 2025-07-08 14:57:06 +03:00
egor-white
2d1a25bbc3 Merge pull request #1 from Likhach42/main
Android 10 fix
2025-07-08 14:47:39 +03:00
egor-white
4737e763ea Merge remote-tracking branch 'origin/main' into a10
# Conflicts:
#	app/build.gradle.kts
#	app/src/main/java/com/cherret/zaprett/MainActivity.kt
#	app/src/main/java/com/cherret/zaprett/ui/viewmodel/BaseListsViewModel.kt
#	app/src/main/java/com/cherret/zaprett/ui/viewmodel/HomeViewModel.kt
#	app/src/main/res/values-ru/strings.xml
#	app/src/main/res/values/strings.xml
#	gradle/libs.versions.toml
2025-07-08 14:39:23 +03:00
egor-white
8c93093b6b Update README.md 2025-07-08 09:44:43 +03:00
egor-white
0977d30729 Add files via upload 2025-07-08 09:43:43 +03:00
egor-white
f02b22d8bd Merge remote-tracking branch 'origin/main' 2025-07-07 14:14:59 +03:00
egor-white
6cf4a067fb fix status card text out of card 2025-07-07 14:14:37 +03:00
CherretGit
3fa009d3e9 Update update.json 2025-07-07 17:59:23 +07:00
CherretGit
0222ee5cfc Update update.json 2025-07-07 17:53:13 +07:00
CherretGit
e8c8a2a6c1 Update Updater.kt 2025-07-07 17:41:27 +07:00
egor-white
7c75174e26 fix installApk crashes 2025-07-07 13:40:00 +03:00
egor-white
c687d912b3 Update update.json 2025-07-07 12:47:07 +03:00
egor-white
6b017d116f Update changelog.md 2025-07-07 12:46:36 +03:00
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
maksim
c153b582e7 Android 10 fix & update libs 2025-06-27 20:19:48 +03:00
29 changed files with 273 additions and 124 deletions

View File

@@ -4,10 +4,10 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-06-22T07:39:53.690794081Z">
<DropdownSelection timestamp="2025-07-08T13:21:50.180843436Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="Default" identifier="serial=10.0.0.189:40463;connection=ffec9c3f" />
<DeviceId pluginId="LocalEmulator" identifier="path=/home/white/.android/avd/Medium_Phone.avd" />
</handle>
</Target>
</DropdownSelection>

View File

@@ -16,3 +16,4 @@
## Скриншоты:
<img src="images/1.png" width="300"><img src="images/2.png" width="300"><img src="images/3.png" width="300"><img src="images/4.png" width="300"><img src="images/5.png" width="300">
<img src="images/6.png" width="300">
<img src="images/7.png" width="300">

View File

@@ -13,10 +13,10 @@ android {
defaultConfig {
applicationId = "com.cherret.zaprett"
minSdk = 30
minSdk = 29
targetSdk = 35
versionCode = 13
versionName = "2.1"
versionCode = 15
versionName = "2.3"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
ndk {

View File

@@ -14,6 +14,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<application
android:requestLegacyExternalStorage="true"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
@@ -45,7 +46,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"

View File

@@ -1,7 +1,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 +18,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 +30,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 +55,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,7 +87,29 @@ class MainActivity : ComponentActivity() {
setContent {
ZaprettTheme {
val sharedPreferences = remember { getSharedPreferences("settings", MODE_PRIVATE) }
var showStoragePermissionDialog by remember { mutableStateOf(!Environment.isExternalStorageManager()) }
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(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
!Environment.isExternalStorageManager()
} else {
ContextCompat.checkSelfPermission(
this,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
}
)
}
var showNotificationPermissionDialog by remember {
mutableStateOf(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
@@ -101,10 +124,20 @@ class MainActivity : ComponentActivity() {
title = stringResource(R.string.error_no_storage_title),
message = stringResource(R.string.error_no_storage_message),
onConfirm = {
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
val uri = Uri.fromParts("package", packageName, null)
intent.data = uri
startActivity(intent)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
val uri = Uri.fromParts("package", applicationContext.packageName, null)
intent.data = uri
startActivity(intent)
} else {
requestPermissions(
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
),
100
)
}
showStoragePermissionDialog = false
},
onDismiss = { showStoragePermissionDialog = false }
@@ -133,6 +166,7 @@ class MainActivity : ComponentActivity() {
}
}
@Composable
fun BottomBar() {
val navController = rememberNavController()

View File

@@ -14,7 +14,7 @@ import android.widget.Toast
import androidx.core.app.NotificationCompat
import com.cherret.zaprett.MainActivity
import com.cherret.zaprett.R
import com.cherret.zaprett.getActiveStrategy
import com.cherret.zaprett.utils.getActiveStrategy
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -40,6 +40,7 @@ class ByeDpiVpnService : VpnService() {
super.onStartCommand(intent, flags, startId)
return when (intent?.action) {
"START_VPN" -> {
startForeground(NOTIFICATION_ID, createNotification())
setupProxy()
START_STICKY
}
@@ -98,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()
}
}

View File

@@ -10,14 +10,17 @@ 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.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
@@ -49,6 +52,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
@@ -58,6 +62,7 @@ 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.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
@@ -75,6 +80,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
@@ -119,7 +125,7 @@ fun HomeScreen(viewModel: HomeViewModel = viewModel(), vpnLauncher: ActivityResu
Column(modifier = Modifier
.padding(paddingValues)
.verticalScroll(rememberScrollState())) {
ServiceStatusCard(viewModel, cardText, snackbarHostState, scope)
ServiceStatusCard(viewModel, cardText, cardIcon, snackbarHostState, scope)
UpdateCard(updateAvailable) { viewModel.showUpdateDialog() }
if (showUpdateDialog) {
UpdateDialog(viewModel, changeLog.value.orEmpty(), newVersion) { viewModel.dismissUpdateDialog() }
@@ -137,25 +143,44 @@ 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),
//.height(150.dp)
.wrapContentHeight(),
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)),
fontSize = 16.sp,
//maxLines = 3,
//overflow = TextOverflow.Ellipsis,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
textAlign = TextAlign.Center
)
}
}
}

View File

@@ -25,10 +25,11 @@ import com.cherret.zaprett.BuildConfig
import com.cherret.zaprett.byedpi.ByeDpiVpnService
import com.cherret.zaprett.R
import com.cherret.zaprett.byedpi.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.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

@@ -2,7 +2,9 @@ package com.cherret.zaprett.ui.viewmodel
import android.app.Application
import android.content.Context
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.OpenableColumns
import androidx.compose.material3.SnackbarHostState
@@ -11,15 +13,17 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.content.ContextCompat
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
import java.io.FileOutputStream
import java.io.IOException
import java.util.jar.Manifest
abstract class BaseListsViewModel(application: Application) : AndroidViewModel(application) {
val context = application
@@ -61,7 +65,11 @@ abstract class BaseListsViewModel(application: Application) : AndroidViewModel(a
}
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 (!Environment.isExternalStorageManager()) return
}
else if (ContextCompat.checkSelfPermission(context, android.Manifest.permission.READ_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(context, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) return
val contentResolver = context.contentResolver
val fileName = contentResolver.query(uri, null, null, null, null)?.use { cursor ->
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
@@ -72,9 +80,29 @@ abstract class BaseListsViewModel(application: Application) : AndroidViewModel(a
if (!directory.exists()) {
directory.mkdirs()
}
val outputFile = File(getZaprettPath() + path, fileName)
try {
val outputFile = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (Environment.isExternalStorageManager()) {
val outputDir = File(getZaprettPath() + path)
if (!outputDir.exists()) {
outputDir.mkdirs()
}
File(outputDir, fileName)
} else {
val outputDir = File(context.filesDir, path)
if (!outputDir.exists()) {
outputDir.mkdirs()
}
File(outputDir, fileName)
}
} else {
val outputDir = File(context.filesDir, path)
if (!outputDir.exists()) {
outputDir.mkdirs()
}
File(outputDir, fileName)
}
contentResolver.openInputStream(uri)?.use { inputStream ->
FileOutputStream(outputFile).use { outputStream ->
inputStream.copyTo(outputStream)

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,6 +3,16 @@ package com.cherret.zaprett.ui.viewmodel
import android.app.Application
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Debug
import android.provider.Settings
import android.util.Log
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
@@ -11,17 +21,18 @@ import androidx.lifecycle.AndroidViewModel
import com.cherret.zaprett.byedpi.ByeDpiVpnService
import com.cherret.zaprett.R
import com.cherret.zaprett.byedpi.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.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 +45,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
@@ -77,21 +90,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.intValue = R.string.status_enabled
cardIcon.value = Icons.Filled.CheckCircle
}
else {
cardText.intValue = 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.intValue = R.string.status_enabled
cardIcon.value = Icons.Filled.CheckCircle
}
else {
cardText.intValue = 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.intValue = 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 +154,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 {
@@ -196,9 +246,17 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
fun onUpdateConfirm() {
showUpdateDialog.value = false
val id = download(context, downloadUrl.value.orEmpty())
registerDownloadListener(context, id) { uri ->
installApk(context, uri)
if (context.packageManager.canRequestPackageInstalls()){
val id = download(context, downloadUrl.value.orEmpty())
registerDownloadListener(context, id) { uri ->
installApk(context, uri)
}
}
else {
val packageUri = Uri.fromParts("package", context.packageName, null)
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageUri).addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
}
}

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

@@ -6,13 +6,13 @@ import android.content.SharedPreferences
import androidx.compose.material3.SnackbarHostState
import com.cherret.zaprett.byedpi.ByeDpiVpnService
import com.cherret.zaprett.byedpi.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.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

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,18 +1,22 @@
package com.cherret.zaprett
package com.cherret.zaprett.utils
import android.annotation.SuppressLint
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.provider.Settings
import android.util.Log
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
@@ -77,7 +81,22 @@ fun download(context: Context, url: String): Long {
setTitle(fileName)
setDescription(fileName)
setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
val contentValues = ContentValues().apply {
put(MediaStore.Downloads.DISPLAY_NAME, fileName)
put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
}
val uri = context.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
if (uri != null) {
setDestinationUri(uri)
} else {
Log.e("Updater", "Failed to create MediaStore URI")
return -1L
}
} else {
setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
}
setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
}
return downloadManager.enqueue(request)
@@ -93,11 +112,6 @@ fun installApk(context: Context, uri: Uri) {
}
context.startActivity(intent)
}
else {
val packageUri = Uri.fromParts("package", context.packageName, null)
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageUri)
context.startActivity(intent)
}
}
fun registerDownloadListener(context: Context, downloadId: Long, onDownloaded: (Uri) -> Unit) {// AI Generated

View File

@@ -1,3 +1 @@
Исправление багов в фрагменте хостов
Исправление сплющивания карточки информации о модуле
Перенос версии byedpi в соответствующее место
Поддержка Android 10(спасибо @Likhach42)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 113 KiB

BIN
images/7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

View File

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