mirror of
https://github.com/openlibrecommunity/olcng.git
synced 2026-07-03 14:05:17 +02:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f58a4f8f6f | |||
| fdce3ea2c1 | |||
| f0d620676d | |||
| b92a6cdfac | |||
| 45da2479dd | |||
| 1c936b2b31 | |||
| dbe109eedb | |||
| 7705aded77 | |||
| 4a2d62b671 | |||
| 4acca4e554 | |||
| b875613fb3 | |||
| d9d21061fa | |||
| fc7804fc1e | |||
| 4e9de615a4 | |||
| 7617ce898c | |||
| 8d284fd68a | |||
| 0f28310801 | |||
| a46123aeab | |||
| 06579df2a4 | |||
| 56532f10d3 | |||
| e843b92ef2 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -30,7 +30,8 @@
|
||||
"enabled": true,
|
||||
"destOverride": [
|
||||
"http",
|
||||
"tls"
|
||||
"tls",
|
||||
"quic"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
"enabled": true,
|
||||
"destOverride": [
|
||||
"http",
|
||||
"tls"
|
||||
"tls",
|
||||
"quic"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -47,7 +48,8 @@
|
||||
"enabled": true,
|
||||
"destOverride": [
|
||||
"http",
|
||||
"tls"
|
||||
"tls",
|
||||
"quic"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,8 +123,7 @@ object AppConfig {
|
||||
// const val IP_API_URL = "https://speed.cloudflare.com/meta"
|
||||
const val IP_API_URL = "https://api.ip.sb/geoip"
|
||||
|
||||
/** DNS server addresses. */
|
||||
const val DNS_PROXY = "1.1.1.1"
|
||||
const val DNS_PROXY = "https://1.1.1.1/dns-query"
|
||||
const val DNS_DIRECT = "223.5.5.5"
|
||||
const val DNS_VPN = "1.1.1.1"
|
||||
const val GEOSITE_PRIVATE = "geosite:private"
|
||||
|
||||
@@ -273,26 +273,35 @@ object AngConfigManager {
|
||||
private fun batchSaveConfigs(configs: List<ProfileItem>, subid: String): Map<String, ProfileItem> {
|
||||
val keyToProfile = mutableMapOf<String, ProfileItem>()
|
||||
|
||||
// Read serverList once
|
||||
val serverList = MmkvManager.decodeServerList(subid)
|
||||
var needSetSelected = MmkvManager.getSelectServer().isNullOrBlank()
|
||||
|
||||
configs.forEach { config ->
|
||||
val key = Utils.getUuid()
|
||||
// Save profile directly without updating serverList
|
||||
MmkvManager.encodeProfileDirect(key, JsonUtil.toJson(config))
|
||||
val existingProfiles = serverList.mapNotNull { guid ->
|
||||
MmkvManager.decodeServerConfig(guid)?.let { guid to it }
|
||||
}.toMap()
|
||||
|
||||
if (!serverList.contains(key)) {
|
||||
serverList.add(0, key)
|
||||
if (needSetSelected) {
|
||||
MmkvManager.setSelectServer(key)
|
||||
needSetSelected = false
|
||||
configs.forEach { config ->
|
||||
val existingKey = existingProfiles.entries.firstOrNull { (_, existing) ->
|
||||
existing == config
|
||||
}?.key
|
||||
|
||||
if (existingKey != null) {
|
||||
keyToProfile[existingKey] = config
|
||||
} else {
|
||||
val key = Utils.getUuid()
|
||||
MmkvManager.encodeProfileDirect(key, JsonUtil.toJson(config))
|
||||
|
||||
if (!serverList.contains(key)) {
|
||||
serverList.add(0, key)
|
||||
if (needSetSelected) {
|
||||
MmkvManager.setSelectServer(key)
|
||||
needSetSelected = false
|
||||
}
|
||||
}
|
||||
keyToProfile[key] = config
|
||||
}
|
||||
keyToProfile[key] = config
|
||||
}
|
||||
|
||||
// Write serverList once
|
||||
MmkvManager.encodeServerList(serverList, subid)
|
||||
return keyToProfile
|
||||
}
|
||||
|
||||
@@ -346,7 +346,7 @@ object SettingsManager {
|
||||
*/
|
||||
fun getVpnDnsServers(): List<String> {
|
||||
val vpnDns = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_DNS) ?: AppConfig.DNS_VPN
|
||||
return vpnDns.split(",").filter { Utils.isPureIpAddress(it) }
|
||||
return vpnDns.split(",").filter { it.isNotBlank() && Utils.isPureIpAddress(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -438,7 +438,6 @@ object SettingsManager {
|
||||
* Ensure default settings are present in MMKV.
|
||||
*/
|
||||
private fun ensureDefaultSettings() {
|
||||
// Write defaults in the exact order requested by the user
|
||||
ensureDefaultValue(AppConfig.PREF_MODE, AppConfig.VPN)
|
||||
ensureDefaultValue(AppConfig.PREF_VPN_DNS, AppConfig.DNS_VPN)
|
||||
ensureDefaultValue(AppConfig.PREF_VPN_MTU, AppConfig.VPN_MTU.toString())
|
||||
@@ -453,6 +452,9 @@ object SettingsManager {
|
||||
ensureDefaultValue(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "8")
|
||||
ensureDefaultValue(AppConfig.PREF_FRAGMENT_LENGTH, "50-100")
|
||||
ensureDefaultValue(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20")
|
||||
|
||||
ensureDefaultBoolValue(AppConfig.PREF_LOCAL_DNS_ENABLED, true)
|
||||
ensureDefaultBoolValue(AppConfig.PREF_FAKE_DNS_ENABLED, true)
|
||||
}
|
||||
|
||||
private fun ensureDefaultValue(key: String, default: String) {
|
||||
@@ -461,6 +463,12 @@ object SettingsManager {
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureDefaultBoolValue(key: String, default: Boolean) {
|
||||
if (MmkvManager.decodeSettingsBool(key) == null) {
|
||||
MmkvManager.encodeSettings(key, default)
|
||||
}
|
||||
}
|
||||
|
||||
private fun migrateAutoSort() {
|
||||
val migrationKey = "auto_sort_migrated_v2"
|
||||
if (MmkvManager.decodeSettingsBool(migrationKey, false)) {
|
||||
|
||||
@@ -94,18 +94,23 @@ object SpeedtestManager {
|
||||
var result: String
|
||||
var elapsed = -1L
|
||||
|
||||
val conn = HttpUtil.createProxyConnection(SettingsManager.getDelayTestUrl(), port, 15000, 15000) ?: return Pair(elapsed, "")
|
||||
val testUrl = "https://icanhazip.com"
|
||||
val conn = HttpUtil.createProxyConnection(testUrl, port, 15000, 15000) ?: return Pair(elapsed, "")
|
||||
try {
|
||||
val start = SystemClock.elapsedRealtime()
|
||||
val code = conn.responseCode
|
||||
|
||||
if (code != 200) {
|
||||
throw IOException(context.getString(R.string.connection_test_error_status_code, code))
|
||||
}
|
||||
|
||||
val responseBody = conn.inputStream.bufferedReader().readText().trim()
|
||||
elapsed = SystemClock.elapsedRealtime() - start
|
||||
|
||||
result = when (code) {
|
||||
204 -> context.getString(R.string.connection_test_available, elapsed)
|
||||
200 if conn.contentLengthLong == 0L -> context.getString(R.string.connection_test_available, elapsed)
|
||||
else -> throw IOException(
|
||||
context.getString(R.string.connection_test_error_status_code, code)
|
||||
)
|
||||
|
||||
if (xyz.zarazaex.olc.util.Utils.isPureIpAddress(responseBody)) {
|
||||
result = context.getString(R.string.connection_test_available, elapsed)
|
||||
} else {
|
||||
throw IOException("Invalid IP response: $responseBody")
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.e(AppConfig.TAG, "Connection test IOException", e)
|
||||
|
||||
@@ -30,6 +30,8 @@ object V2RayServiceManager {
|
||||
private val coreController: CoreController = V2RayNativeManager.newCoreController(CoreCallback())
|
||||
private val mMsgReceive = ReceiveMessageHandler()
|
||||
private var currentConfig: ProfileItem? = null
|
||||
private val operationLock = Any()
|
||||
@Volatile private var isOperationInProgress = false
|
||||
|
||||
var serviceControl: SoftReference<ServiceControl>? = null
|
||||
set(value) {
|
||||
@@ -57,13 +59,27 @@ object V2RayServiceManager {
|
||||
* @param guid The GUID of the server configuration to use (optional).
|
||||
*/
|
||||
fun startVService(context: Context, guid: String? = null) {
|
||||
Log.i(AppConfig.TAG, "StartCore-Manager: startVService from ${context::class.java.simpleName}")
|
||||
|
||||
if (guid != null) {
|
||||
MmkvManager.setSelectServer(guid)
|
||||
synchronized(operationLock) {
|
||||
if (isOperationInProgress) {
|
||||
Log.w(AppConfig.TAG, "StartCore-Manager: Operation already in progress")
|
||||
return
|
||||
}
|
||||
isOperationInProgress = true
|
||||
}
|
||||
|
||||
startContextService(context)
|
||||
try {
|
||||
Log.i(AppConfig.TAG, "StartCore-Manager: startVService from ${context::class.java.simpleName}")
|
||||
|
||||
if (guid != null) {
|
||||
MmkvManager.setSelectServer(guid)
|
||||
}
|
||||
|
||||
startContextService(context)
|
||||
} finally {
|
||||
synchronized(operationLock) {
|
||||
isOperationInProgress = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,8 +87,21 @@ object V2RayServiceManager {
|
||||
* @param context The context from which the service is stopped.
|
||||
*/
|
||||
fun stopVService(context: Context) {
|
||||
//context.toast(R.string.toast_services_stop)
|
||||
MessageUtil.sendMsg2Service(context, AppConfig.MSG_STATE_STOP, "")
|
||||
synchronized(operationLock) {
|
||||
if (isOperationInProgress) {
|
||||
Log.w(AppConfig.TAG, "StartCore-Manager: Operation already in progress")
|
||||
return
|
||||
}
|
||||
isOperationInProgress = true
|
||||
}
|
||||
|
||||
try {
|
||||
MessageUtil.sendMsg2Service(context, AppConfig.MSG_STATE_STOP, "")
|
||||
} finally {
|
||||
synchronized(operationLock) {
|
||||
isOperationInProgress = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,11 +414,17 @@ object V2RayServiceManager {
|
||||
|
||||
AppConfig.MSG_STATE_STOP -> {
|
||||
Log.i(AppConfig.TAG, "StartCore-Manager: Stop service")
|
||||
synchronized(operationLock) {
|
||||
isOperationInProgress = false
|
||||
}
|
||||
serviceControl.stopService()
|
||||
}
|
||||
|
||||
AppConfig.MSG_STATE_RESTART -> {
|
||||
Log.i(AppConfig.TAG, "StartCore-Manager: Restart service")
|
||||
synchronized(operationLock) {
|
||||
isOperationInProgress = false
|
||||
}
|
||||
serviceControl.stopService()
|
||||
Thread.sleep(500L)
|
||||
startVService(serviceControl.getService())
|
||||
|
||||
@@ -592,12 +592,13 @@ object V2rayConfigManager {
|
||||
val hosts = mutableMapOf<String, Any>()
|
||||
val servers = ArrayList<Any>()
|
||||
|
||||
//remote Dns
|
||||
val remoteDns = SettingsManager.getRemoteDnsServers()
|
||||
val domesticDns = SettingsManager.getDomesticDnsServers()
|
||||
val proxyDomain = getUserRule2Domain(AppConfig.TAG_PROXY)
|
||||
remoteDns.forEach {
|
||||
servers.add(it)
|
||||
}
|
||||
val directDomain = getUserRule2Domain(AppConfig.TAG_DIRECT)
|
||||
val isCnRoutingMode = directDomain.contains(AppConfig.GEOSITE_CN)
|
||||
val geoipCn = arrayListOf(AppConfig.GEOIP_CN)
|
||||
|
||||
if (proxyDomain.isNotEmpty()) {
|
||||
servers.add(
|
||||
V2rayConfig.DnsBean.ServersBean(
|
||||
@@ -607,11 +608,6 @@ object V2rayConfigManager {
|
||||
)
|
||||
}
|
||||
|
||||
// domestic DNS
|
||||
val domesticDns = SettingsManager.getDomesticDnsServers()
|
||||
val directDomain = getUserRule2Domain(AppConfig.TAG_DIRECT)
|
||||
val isCnRoutingMode = directDomain.contains(AppConfig.GEOSITE_CN)
|
||||
val geoipCn = arrayListOf(AppConfig.GEOIP_CN)
|
||||
if (directDomain.isNotEmpty()) {
|
||||
servers.add(
|
||||
V2rayConfig.DnsBean.ServersBean(
|
||||
@@ -624,16 +620,17 @@ object V2rayConfigManager {
|
||||
)
|
||||
}
|
||||
|
||||
//block dns
|
||||
remoteDns.forEach {
|
||||
servers.add(it)
|
||||
}
|
||||
|
||||
val blkDomain = getUserRule2Domain(AppConfig.TAG_BLOCKED)
|
||||
if (blkDomain.isNotEmpty()) {
|
||||
hosts.putAll(blkDomain.map { it to AppConfig.LOOPBACK })
|
||||
}
|
||||
|
||||
// hardcode googleapi rule to fix play store problems
|
||||
hosts[AppConfig.GOOGLEAPIS_CN_DOMAIN] = AppConfig.GOOGLEAPIS_COM_DOMAIN
|
||||
|
||||
// hardcode popular Android Private DNS rule to fix localhost DNS problem
|
||||
hosts[AppConfig.DNS_ALIDNS_DOMAIN] = AppConfig.DNS_ALIDNS_ADDRESSES
|
||||
hosts[AppConfig.DNS_CLOUDFLARE_ONE_DOMAIN] = AppConfig.DNS_CLOUDFLARE_ONE_ADDRESSES
|
||||
hosts[AppConfig.DNS_CLOUDFLARE_DNS_COM_DOMAIN] = AppConfig.DNS_CLOUDFLARE_DNS_COM_ADDRESSES
|
||||
@@ -643,7 +640,6 @@ object V2rayConfigManager {
|
||||
hosts[AppConfig.DNS_QUAD9_DOMAIN] = AppConfig.DNS_QUAD9_ADDRESSES
|
||||
hosts[AppConfig.DNS_YANDEX_DOMAIN] = AppConfig.DNS_YANDEX_ADDRESSES
|
||||
|
||||
//User DNS hosts
|
||||
try {
|
||||
val userHosts = MmkvManager.decodeSettingsString(AppConfig.PREF_DNS_HOSTS)
|
||||
if (userHosts.isNotNullEmpty()) {
|
||||
@@ -657,14 +653,12 @@ object V2rayConfigManager {
|
||||
Log.e(AppConfig.TAG, "Failed to configure user DNS hosts", e)
|
||||
}
|
||||
|
||||
// DNS dns
|
||||
v2rayConfig.dns = V2rayConfig.DnsBean(
|
||||
servers = servers,
|
||||
hosts = hosts,
|
||||
tag = AppConfig.TAG_DNS
|
||||
)
|
||||
|
||||
// DNS routing
|
||||
v2rayConfig.routing.rules.add(
|
||||
RulesBean(
|
||||
outboundTag = AppConfig.TAG_DIRECT,
|
||||
@@ -1317,7 +1311,7 @@ object V2rayConfigManager {
|
||||
if (start != null && end != null) {
|
||||
val minStart = maxOf(5, start)
|
||||
val minEnd = maxOf(minStart, end)
|
||||
"$minStart-$minEnd"
|
||||
"$minStart-$minEnd"
|
||||
} else {
|
||||
"30"
|
||||
}
|
||||
|
||||
@@ -228,13 +228,13 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
||||
}
|
||||
}
|
||||
|
||||
// Configure DNS servers
|
||||
//if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) {
|
||||
// builder.addDnsServer(PRIVATE_VLAN4_ROUTER)
|
||||
//} else {
|
||||
SettingsManager.getVpnDnsServers().forEach {
|
||||
if (Utils.isPureIpAddress(it)) {
|
||||
builder.addDnsServer(it)
|
||||
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) {
|
||||
builder.addDnsServer(vpnConfig.ipv4Router)
|
||||
} else {
|
||||
SettingsManager.getVpnDnsServers().forEach {
|
||||
if (Utils.isPureIpAddress(it)) {
|
||||
builder.addDnsServer(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,9 +42,7 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelectedListener {
|
||||
private val binding by lazy {
|
||||
ActivityMainBinding.inflate(layoutInflater)
|
||||
}
|
||||
private val binding by lazy {ActivityMainBinding.inflate(layoutInflater)}
|
||||
private var isLiteTesting = false
|
||||
private var easterEggClickCount = 0
|
||||
private var isEasterEggActive = false
|
||||
@@ -52,6 +50,7 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
val mainViewModel: MainViewModel by viewModels()
|
||||
private lateinit var groupPagerAdapter: GroupPagerAdapter
|
||||
private var tabMediator: TabLayoutMediator? = null
|
||||
@Volatile private var isFabOperationInProgress = false
|
||||
|
||||
private val requestVpnPermission = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == RESULT_OK) {
|
||||
@@ -201,12 +200,24 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
}
|
||||
|
||||
private fun handleFabAction() {
|
||||
if (isFabOperationInProgress) {
|
||||
return
|
||||
}
|
||||
isFabOperationInProgress = true
|
||||
|
||||
applyRunningState(isLoading = true, isRunning = false)
|
||||
|
||||
if (mainViewModel.isRunning.value == true) {
|
||||
V2RayServiceManager.stopVService(this)
|
||||
} else {
|
||||
startV2RayWithPermission()
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
if (mainViewModel.isRunning.value == true) {
|
||||
V2RayServiceManager.stopVService(this@MainActivity)
|
||||
} else {
|
||||
startV2RayWithPermission()
|
||||
}
|
||||
} finally {
|
||||
delay(1000)
|
||||
isFabOperationInProgress = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,29 +231,42 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
}
|
||||
|
||||
private fun handleLiteAction() {
|
||||
if (mainViewModel.isRunning.value == true) {
|
||||
V2RayServiceManager.stopVService(this)
|
||||
if (isFabOperationInProgress) {
|
||||
return
|
||||
}
|
||||
|
||||
showStatus("Обновление профилей...")
|
||||
showLoading()
|
||||
isLiteTesting = true
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val result = mainViewModel.updateConfigViaSubAll()
|
||||
delay(500L)
|
||||
launch(Dispatchers.Main) {
|
||||
if (result.configCount > 0) {
|
||||
mainViewModel.reloadServerList()
|
||||
showStatus("Обновлено ${result.configCount} профилей. Запуск теста...")
|
||||
} else {
|
||||
showStatus("Запуск теста...")
|
||||
isFabOperationInProgress = true
|
||||
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
if (mainViewModel.isRunning.value == true) {
|
||||
V2RayServiceManager.stopVService(this@MainActivity)
|
||||
delay(1000)
|
||||
}
|
||||
hideLoading()
|
||||
|
||||
delay(500L)
|
||||
showStatus("Выполняется замер задержки. Ожидаем завершения...")
|
||||
mainViewModel.testAllRealPing()
|
||||
|
||||
showStatus("Обновление профилей...")
|
||||
showLoading()
|
||||
isLiteTesting = true
|
||||
|
||||
launch(Dispatchers.IO) {
|
||||
val result = mainViewModel.updateConfigViaSubAll()
|
||||
delay(500L)
|
||||
launch(Dispatchers.Main) {
|
||||
if (result.configCount > 0) {
|
||||
mainViewModel.reloadServerList()
|
||||
showStatus("Обновлено ${result.configCount} профилей. Запуск теста...")
|
||||
} else {
|
||||
showStatus("Запуск теста...")
|
||||
}
|
||||
hideLoading()
|
||||
|
||||
delay(500L)
|
||||
showStatus("Выполняется замер задержки. Ожидаем завершения...")
|
||||
mainViewModel.testAllRealPing()
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
delay(1000)
|
||||
isFabOperationInProgress = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,12 +293,22 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
}
|
||||
|
||||
fun restartV2Ray() {
|
||||
if (mainViewModel.isRunning.value == true) {
|
||||
V2RayServiceManager.stopVService(this)
|
||||
if (isFabOperationInProgress) {
|
||||
return
|
||||
}
|
||||
isFabOperationInProgress = true
|
||||
|
||||
lifecycleScope.launch {
|
||||
delay(500)
|
||||
startV2Ray()
|
||||
try {
|
||||
if (mainViewModel.isRunning.value == true) {
|
||||
V2RayServiceManager.stopVService(this@MainActivity)
|
||||
}
|
||||
delay(1000)
|
||||
startV2Ray()
|
||||
} finally {
|
||||
delay(500)
|
||||
isFabOperationInProgress = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
<string name="navigation_drawer_open">Open navigation drawer</string>
|
||||
<string name="navigation_drawer_close">Close navigation drawer</string>
|
||||
<string name="migration_success">Data migration success!</string>
|
||||
<string name="drawer_forked_text">forked from <a href="https://github.com/2dust/v2rayng">V2RayNG</a></string>
|
||||
<string name="drawer_developed_text">developed by developers from <a href="https://t.me/openlibrecommunity">Olc</a></string>
|
||||
<string name="drawer_forked_text" translatable="false">forked from <a href="https://github.com/2dust/v2rayng">V2RayNG</a></string>
|
||||
<string name="drawer_developed_text" translatable="false">developed by developers from <a href="https://t.me/openlibrecommunity">Olc</a></string>
|
||||
<string name="action_stop_service">Stop service</string>
|
||||
<string name="migration_fail">Data migration failed!</string>
|
||||
<string name="pull_down_to_refresh">Please pull down to refresh!</string>
|
||||
@@ -249,7 +249,7 @@
|
||||
<string name="summary_pref_tg_group">Join Telegram Group</string>
|
||||
<string name="toast_tg_app_not_found">Telegram app not found</string>
|
||||
<string name="title_privacy_policy">Privacy policy</string>
|
||||
<string name="title_qr_code">QR code</string>
|
||||
<string name="title_qr_code" translatable="false">QR code</string>
|
||||
<string name="title_about">About</string>
|
||||
<string name="title_source_code">Source code</string>
|
||||
<string name="title_oss_license">Open Source licenses</string>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
agp = "9.1.0"
|
||||
desugarJdkLibs = "2.1.5"
|
||||
gradleLicensePlugin = "0.9.8"
|
||||
kotlin = "2.3.10"
|
||||
kotlin = "2.1.0"
|
||||
coreKtx = "1.17.0"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.3.0"
|
||||
|
||||
@@ -136,9 +136,17 @@ fun main() {
|
||||
)
|
||||
println("Добавлена подписка БЕЛЫЕ W: $guid2")
|
||||
|
||||
val guid3 = manager.addSubscription(
|
||||
remarks = "KEY",
|
||||
url = "https://key.zarazaex.xyz/sub",
|
||||
autoUpdate = true
|
||||
)
|
||||
println("Добавлена подписка KEY: $guid3")
|
||||
|
||||
println("\nОбновление подписок...")
|
||||
manager.updateSubscription(guid1, "https://raw.githubusercontent.com/zieng2/wl/refs/heads/main/vless_universal.txt")
|
||||
manager.updateSubscription(guid2, "https://raw.githubusercontent.com/whoahaow/rjsxrd/refs/heads/main/githubmirror/bypass/bypass-all.txt")
|
||||
manager.updateSubscription(guid3, "https://key.zarazaex.xyz/sub")
|
||||
|
||||
println("\nПодписки успешно добавлены и обновлены в $mmkvPath")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user