Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66f100ebb3 | |||
| b0213ffdf6 | |||
| 538b2eb0f8 | |||
| f2093c4c52 | |||
| 02ac57a4c6 | |||
| 379266f205 | |||
| 23dbb35da7 |
@@ -12,8 +12,8 @@ android {
|
|||||||
applicationId = "com.v2ray.ang"
|
applicationId = "com.v2ray.ang"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 701
|
versionCode = 702
|
||||||
versionName = "2.0.1"
|
versionName = "2.0.2"
|
||||||
multiDexEnabled = true
|
multiDexEnabled = true
|
||||||
|
|
||||||
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
|
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
|
||||||
|
|||||||
@@ -4,6 +4,5 @@ data class ConfigResult(
|
|||||||
var status: Boolean,
|
var status: Boolean,
|
||||||
var guid: String? = null,
|
var guid: String? = null,
|
||||||
var content: String = "",
|
var content: String = "",
|
||||||
var socksPort: Int? = null,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ inline fun <reified T : Serializable> Intent.serializable(key: String): T? = whe
|
|||||||
*
|
*
|
||||||
* @return True if the CharSequence is not null and not empty, false otherwise.
|
* @return True if the CharSequence is not null and not empty, false otherwise.
|
||||||
*/
|
*/
|
||||||
fun CharSequence?.isNotNullEmpty(): Boolean = this != null && this.isNotEmpty()
|
fun CharSequence?.isNotNullEmpty(): Boolean = !this.isNullOrBlank()
|
||||||
|
|
||||||
fun String.concatUrl(vararg paths: String): String {
|
fun String.concatUrl(vararg paths: String): String {
|
||||||
val builder = StringBuilder(this.trimEnd('/'))
|
val builder = StringBuilder(this.trimEnd('/'))
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.v2ray.ang.handler
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.google.gson.JsonArray
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.dto.ConfigResult
|
import com.v2ray.ang.dto.ConfigResult
|
||||||
import com.v2ray.ang.dto.EConfigType
|
import com.v2ray.ang.dto.EConfigType
|
||||||
@@ -44,7 +45,7 @@ object V2rayConfigManager {
|
|||||||
try {
|
try {
|
||||||
val config = MmkvManager.decodeServerConfig(guid) ?: return ConfigResult(false)
|
val config = MmkvManager.decodeServerConfig(guid) ?: return ConfigResult(false)
|
||||||
return if (config.configType == EConfigType.CUSTOM) {
|
return if (config.configType == EConfigType.CUSTOM) {
|
||||||
getV2rayCustomConfig(guid, config)
|
getV2rayCustomConfig(context, guid, config)
|
||||||
} else if (config.configType == EConfigType.POLICYGROUP) {
|
} else if (config.configType == EConfigType.POLICYGROUP) {
|
||||||
getV2rayGroupConfig(context, guid, config)
|
getV2rayGroupConfig(context, guid, config)
|
||||||
} else {
|
} else {
|
||||||
@@ -67,7 +68,7 @@ object V2rayConfigManager {
|
|||||||
try {
|
try {
|
||||||
val config = MmkvManager.decodeServerConfig(guid) ?: return ConfigResult(false)
|
val config = MmkvManager.decodeServerConfig(guid) ?: return ConfigResult(false)
|
||||||
return if (config.configType == EConfigType.CUSTOM) {
|
return if (config.configType == EConfigType.CUSTOM) {
|
||||||
getV2rayCustomConfig(guid, config)
|
getV2rayCustomConfig(context, guid, config)
|
||||||
} else if (config.configType == EConfigType.POLICYGROUP) {
|
} else if (config.configType == EConfigType.POLICYGROUP) {
|
||||||
// The number of policy groups will not be very large, so no special handling is needed.
|
// The number of policy groups will not be very large, so no special handling is needed.
|
||||||
getV2rayGroupConfig(context, guid, config)
|
getV2rayGroupConfig(context, guid, config)
|
||||||
@@ -87,9 +88,43 @@ object V2rayConfigManager {
|
|||||||
* @param config The profile item containing the configuration details.
|
* @param config The profile item containing the configuration details.
|
||||||
* @return A ConfigResult object containing the result of the configuration retrieval.
|
* @return A ConfigResult object containing the result of the configuration retrieval.
|
||||||
*/
|
*/
|
||||||
private fun getV2rayCustomConfig(guid: String, config: ProfileItem): ConfigResult {
|
private fun getV2rayCustomConfig(context: Context, guid: String, config: ProfileItem): ConfigResult {
|
||||||
val raw = MmkvManager.decodeServerRaw(guid) ?: return ConfigResult(false)
|
val raw = MmkvManager.decodeServerRaw(guid) ?: return ConfigResult(false)
|
||||||
return ConfigResult(true, guid, raw)
|
val result = ConfigResult(true, guid, raw)
|
||||||
|
if (SettingsManager.isUsingHevTun()) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if tun inbound exists
|
||||||
|
val json = JsonUtil.parseString(raw) ?: return result
|
||||||
|
val inboundsJson = if (json.has("inbounds") && json.get("inbounds")?.isJsonNull == false) {
|
||||||
|
json.getAsJsonArray("inbounds")
|
||||||
|
} else {
|
||||||
|
JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in 0 until inboundsJson.size()) {
|
||||||
|
val elem = inboundsJson.get(i)
|
||||||
|
if (elem.isJsonObject) {
|
||||||
|
val inb = elem.asJsonObject
|
||||||
|
val tag = if (inb.has("tag") && inb.get("tag")?.isJsonNull == false) inb.get("tag").asString else ""
|
||||||
|
if (tag == "tun") return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add tun inbound from template
|
||||||
|
val templateConfig = initV2rayConfig(context) ?: return result
|
||||||
|
val inboundTun = templateConfig.inbounds.firstOrNull { it.tag == "tun" } ?: return result
|
||||||
|
inboundTun.settings?.mtu = SettingsManager.getVpnMtu()
|
||||||
|
|
||||||
|
// add to json
|
||||||
|
inboundsJson.add(JsonUtil.parseString(JsonUtil.toJson(inboundTun)))
|
||||||
|
if (inboundsJson.size() == 1) {
|
||||||
|
json.add("inbounds", inboundsJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
val updatedRaw = JsonUtil.toJsonPretty(json) ?: return result
|
||||||
|
return ConfigResult(true, guid, updatedRaw)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1192,7 +1227,11 @@ object V2rayConfigManager {
|
|||||||
if (profileItem.portHopping.isNotNullEmpty()) {
|
if (profileItem.portHopping.isNotNullEmpty()) {
|
||||||
hysteriaSetting.udphop = StreamSettingsBean.HysteriaSettingsBean.HysteriaUdpHopBean(
|
hysteriaSetting.udphop = StreamSettingsBean.HysteriaSettingsBean.HysteriaUdpHopBean(
|
||||||
port = profileItem.portHopping,
|
port = profileItem.portHopping,
|
||||||
interval = profileItem.portHoppingInterval?.ifEmpty { "30" }.orEmpty().toInt()
|
interval = profileItem.portHoppingInterval
|
||||||
|
?.trim()
|
||||||
|
?.toIntOrNull()
|
||||||
|
?.takeIf { it >= 5 }
|
||||||
|
?: 30
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
streamSettings.hysteriaSettings = hysteriaSetting
|
streamSettings.hysteriaSettings = hysteriaSetting
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package com.v2ray.ang.service
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.v2ray.ang.AppConfig
|
||||||
|
import com.v2ray.ang.handler.SettingsManager
|
||||||
|
import com.v2ray.ang.handler.V2RayNativeManager
|
||||||
|
import com.v2ray.ang.handler.V2rayConfigManager
|
||||||
|
import com.v2ray.ang.util.MessageUtil
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.CoroutineName
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.asCoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.joinAll
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Worker that runs a batch of real-ping tests independently.
|
||||||
|
* Each batch owns its own CoroutineScope/dispatcher and can be cancelled separately.
|
||||||
|
*/
|
||||||
|
class RealPingWorkerService(
|
||||||
|
private val context: Context,
|
||||||
|
private val guids: List<String>,
|
||||||
|
private val onFinish: (status: String) -> Unit = {}
|
||||||
|
) {
|
||||||
|
private val job = SupervisorJob()
|
||||||
|
private val cpu = Runtime.getRuntime().availableProcessors().coerceAtLeast(1)
|
||||||
|
private val dispatcher = Executors.newFixedThreadPool(cpu * 4).asCoroutineDispatcher()
|
||||||
|
private val scope = CoroutineScope(job + dispatcher + CoroutineName("RealPingBatchWorker"))
|
||||||
|
|
||||||
|
private val runningCount = AtomicInteger(0)
|
||||||
|
private val totalCount = AtomicInteger(0)
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
val jobs = guids.map { guid ->
|
||||||
|
totalCount.incrementAndGet()
|
||||||
|
scope.launch {
|
||||||
|
runningCount.incrementAndGet()
|
||||||
|
try {
|
||||||
|
val result = startRealPing(guid)
|
||||||
|
MessageUtil.sendMsg2UI(context, AppConfig.MSG_MEASURE_CONFIG_SUCCESS, Pair(guid, result))
|
||||||
|
} finally {
|
||||||
|
val count = totalCount.decrementAndGet()
|
||||||
|
val left = runningCount.decrementAndGet()
|
||||||
|
MessageUtil.sendMsg2UI(context, AppConfig.MSG_MEASURE_CONFIG_NOTIFY, "$left / $count")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.launch {
|
||||||
|
try {
|
||||||
|
joinAll(*jobs.toTypedArray())
|
||||||
|
onFinish("0")
|
||||||
|
} catch (_: CancellationException) {
|
||||||
|
onFinish("-1")
|
||||||
|
} finally {
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel() {
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun close() {
|
||||||
|
try {
|
||||||
|
dispatcher.close()
|
||||||
|
} catch (_: Throwable) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startRealPing(guid: String): Long {
|
||||||
|
val retFailure = -1L
|
||||||
|
val configResult = V2rayConfigManager.getV2rayConfig4Speedtest(context, guid)
|
||||||
|
if (!configResult.status) {
|
||||||
|
return retFailure
|
||||||
|
}
|
||||||
|
return V2RayNativeManager.measureOutboundDelay(configResult.content, SettingsManager.getDelayTestUrl())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -6,34 +6,15 @@ import android.os.IBinder
|
|||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.AppConfig.MSG_MEASURE_CONFIG
|
import com.v2ray.ang.AppConfig.MSG_MEASURE_CONFIG
|
||||||
import com.v2ray.ang.AppConfig.MSG_MEASURE_CONFIG_CANCEL
|
import com.v2ray.ang.AppConfig.MSG_MEASURE_CONFIG_CANCEL
|
||||||
import com.v2ray.ang.AppConfig.MSG_MEASURE_CONFIG_SUCCESS
|
|
||||||
import com.v2ray.ang.extension.serializable
|
import com.v2ray.ang.extension.serializable
|
||||||
import com.v2ray.ang.handler.SettingsManager
|
|
||||||
import com.v2ray.ang.handler.V2RayNativeManager
|
import com.v2ray.ang.handler.V2RayNativeManager
|
||||||
import com.v2ray.ang.handler.V2rayConfigManager
|
|
||||||
import com.v2ray.ang.util.MessageUtil
|
import com.v2ray.ang.util.MessageUtil
|
||||||
import kotlinx.coroutines.CancellationException
|
import java.util.Collections
|
||||||
import kotlinx.coroutines.CoroutineName
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.SupervisorJob
|
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
|
||||||
import kotlinx.coroutines.cancelChildren
|
|
||||||
import kotlinx.coroutines.joinAll
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
|
||||||
|
|
||||||
class V2RayTestService : Service() {
|
class V2RayTestService : Service() {
|
||||||
private val realTestJob = SupervisorJob()
|
|
||||||
private val cpu = Runtime.getRuntime().availableProcessors().coerceAtLeast(1)
|
|
||||||
private val realDispatcher = Executors.newFixedThreadPool((cpu * 2)).asCoroutineDispatcher()
|
|
||||||
private val realTestScope = CoroutineScope(
|
|
||||||
realTestJob + realDispatcher + CoroutineName("RealTest")
|
|
||||||
)
|
|
||||||
|
|
||||||
// simple counter for currently running tasks
|
// manage active batch workers so each batch is independent and cancellable
|
||||||
private val realTestRunningCount = AtomicInteger(0)
|
private val activeWorkers = Collections.synchronizedList(mutableListOf<RealPingWorkerService>())
|
||||||
private val realTestCount = AtomicInteger(0)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the V2Ray environment.
|
* Initializes the V2Ray environment.
|
||||||
@@ -57,8 +38,10 @@ class V2RayTestService : Service() {
|
|||||||
*/
|
*/
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
realTestJob.cancel()
|
// cancel any active workers
|
||||||
realDispatcher.close()
|
val snapshot = ArrayList(activeWorkers)
|
||||||
|
snapshot.forEach { it.cancel() }
|
||||||
|
activeWorkers.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,64 +56,24 @@ class V2RayTestService : Service() {
|
|||||||
MSG_MEASURE_CONFIG -> {
|
MSG_MEASURE_CONFIG -> {
|
||||||
val guidsList = intent.serializable<ArrayList<String>>("content")
|
val guidsList = intent.serializable<ArrayList<String>>("content")
|
||||||
if (guidsList != null && guidsList.isNotEmpty()) {
|
if (guidsList != null && guidsList.isNotEmpty()) {
|
||||||
startBatchRealPing(guidsList)
|
lateinit var worker: RealPingWorkerService
|
||||||
|
worker = RealPingWorkerService(this, guidsList) { status ->
|
||||||
|
// notify UI and remove the worker from active list when finished
|
||||||
|
MessageUtil.sendMsg2UI(this@V2RayTestService, AppConfig.MSG_MEASURE_CONFIG_FINISH, status)
|
||||||
|
activeWorkers.remove(worker)
|
||||||
|
}
|
||||||
|
activeWorkers.add(worker)
|
||||||
|
worker.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MSG_MEASURE_CONFIG_CANCEL -> {
|
MSG_MEASURE_CONFIG_CANCEL -> {
|
||||||
realTestJob.cancelChildren()
|
// cancel all running batch workers independently
|
||||||
|
val snapshot = ArrayList(activeWorkers)
|
||||||
|
snapshot.forEach { it.cancel() }
|
||||||
|
activeWorkers.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.onStartCommand(intent, flags, startId)
|
return super.onStartCommand(intent, flags, startId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts batch real ping tests.
|
|
||||||
* @param guidsList The list of GUIDs to test.
|
|
||||||
*/
|
|
||||||
private fun startBatchRealPing(guidsList: List<String>) {
|
|
||||||
val jobs = guidsList.map { guid ->
|
|
||||||
realTestCount.incrementAndGet()
|
|
||||||
realTestScope.launch {
|
|
||||||
realTestRunningCount.incrementAndGet()
|
|
||||||
try {
|
|
||||||
val result = startRealPing(guid)
|
|
||||||
MessageUtil.sendMsg2UI(this@V2RayTestService, MSG_MEASURE_CONFIG_SUCCESS, Pair(guid, result))
|
|
||||||
} finally {
|
|
||||||
val count = realTestCount.decrementAndGet()
|
|
||||||
val left = realTestRunningCount.decrementAndGet()
|
|
||||||
MessageUtil.sendMsg2UI(this@V2RayTestService, AppConfig.MSG_MEASURE_CONFIG_NOTIFY, "$left / $count")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
realTestScope.launch {
|
|
||||||
try {
|
|
||||||
joinAll(*jobs.toTypedArray())
|
|
||||||
notifyAllTasksCompleted("0")
|
|
||||||
} catch (_: CancellationException) {
|
|
||||||
notifyAllTasksCompleted("-1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun notifyAllTasksCompleted(status: String) {
|
|
||||||
MessageUtil.sendMsg2UI(this@V2RayTestService, AppConfig.MSG_MEASURE_CONFIG_FINISH, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the real ping test.
|
|
||||||
* @param guid The GUID of the configuration.
|
|
||||||
* @return The ping result.
|
|
||||||
*/
|
|
||||||
private fun startRealPing(guid: String): Long {
|
|
||||||
val retFailure = -1L
|
|
||||||
|
|
||||||
val configResult = V2rayConfigManager.getV2rayConfig4Speedtest(this, guid)
|
|
||||||
if (!configResult.status) {
|
|
||||||
return retFailure
|
|
||||||
}
|
|
||||||
return V2RayNativeManager.measureOutboundDelay(configResult.content, SettingsManager.getDelayTestUrl())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -115,52 +115,20 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
setupToolbar(binding.toolbar,false, getString(R.string.title_server))
|
setupToolbar(binding.toolbar, false, getString(R.string.title_server))
|
||||||
|
|
||||||
binding.fab.setOnClickListener {
|
|
||||||
if (mainViewModel.isRunning.value == true) {
|
|
||||||
V2RayServiceManager.stopVService(this)
|
|
||||||
} else if ((MmkvManager.decodeSettingsString(AppConfig.PREF_MODE) ?: VPN) == VPN) {
|
|
||||||
val intent = VpnService.prepare(this)
|
|
||||||
if (intent == null) {
|
|
||||||
startV2Ray()
|
|
||||||
} else {
|
|
||||||
requestVpnPermission.launch(intent)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
startV2Ray()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.layoutTest.setOnClickListener {
|
|
||||||
if (mainViewModel.isRunning.value == true) {
|
|
||||||
setTestState(getString(R.string.connection_test_testing))
|
|
||||||
mainViewModel.testCurrentServerRealPing()
|
|
||||||
} else {
|
|
||||||
// tv_test_state.text = getString(R.string.connection_test_fail)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// setup viewpager and tablayout
|
||||||
groupPagerAdapter = GroupPagerAdapter(this, emptyList())
|
groupPagerAdapter = GroupPagerAdapter(this, emptyList())
|
||||||
binding.viewPager.adapter = groupPagerAdapter
|
binding.viewPager.adapter = groupPagerAdapter
|
||||||
binding.viewPager.isUserInputEnabled = true
|
binding.viewPager.isUserInputEnabled = true
|
||||||
|
|
||||||
|
// setup navigation drawer
|
||||||
val toggle = ActionBarDrawerToggle(
|
val toggle = ActionBarDrawerToggle(
|
||||||
this, binding.drawerLayout, binding.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close
|
this, binding.drawerLayout, binding.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close
|
||||||
)
|
)
|
||||||
binding.drawerLayout.addDrawerListener(toggle)
|
binding.drawerLayout.addDrawerListener(toggle)
|
||||||
toggle.syncState()
|
toggle.syncState()
|
||||||
binding.navView.setNavigationItemSelectedListener(this)
|
binding.navView.setNavigationItemSelectedListener(this)
|
||||||
|
|
||||||
setupGroupTab()
|
|
||||||
setupViewModel()
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
pendingAction = Action.POST_NOTIFICATIONS
|
|
||||||
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
||||||
override fun handleOnBackPressed() {
|
override fun handleOnBackPressed() {
|
||||||
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
|
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
|
||||||
@@ -172,25 +140,26 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
binding.fab.setOnClickListener { handleFabAction() }
|
||||||
|
binding.layoutTest.setOnClickListener { handleLayoutTestClick() }
|
||||||
|
|
||||||
|
setupGroupTab()
|
||||||
|
setupViewModel()
|
||||||
mainViewModel.reloadServerList()
|
mainViewModel.reloadServerList()
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
pendingAction = Action.POST_NOTIFICATIONS
|
||||||
|
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupViewModel() {
|
private fun setupViewModel() {
|
||||||
mainViewModel.updateTestResultAction.observe(this) { setTestState(it) }
|
mainViewModel.updateTestResultAction.observe(this) { setTestState(it) }
|
||||||
mainViewModel.isRunning.observe(this) { isRunning ->
|
mainViewModel.isRunning.observe(this) { isRunning ->
|
||||||
if (isRunning) {
|
applyRunningState(false, isRunning)
|
||||||
binding.fab.setImageResource(R.drawable.ic_stop_24dp)
|
|
||||||
binding.fab.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_fab_active))
|
|
||||||
binding.fab.contentDescription = getString(R.string.action_stop_service)
|
|
||||||
setTestState(getString(R.string.connection_connected))
|
|
||||||
binding.layoutTest.isFocusable = true
|
|
||||||
} else {
|
|
||||||
binding.fab.setImageResource(R.drawable.ic_play_24dp)
|
|
||||||
binding.fab.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_fab_inactive))
|
|
||||||
binding.fab.contentDescription = getString(R.string.tasker_start_service)
|
|
||||||
setTestState(getString(R.string.connection_not_connected))
|
|
||||||
binding.layoutTest.isFocusable = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mainViewModel.startListenBroadcast()
|
mainViewModel.startListenBroadcast()
|
||||||
mainViewModel.initAssets(assets)
|
mainViewModel.initAssets(assets)
|
||||||
@@ -214,6 +183,32 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
binding.tabGroup.isVisible = groups.size > 1
|
binding.tabGroup.isVisible = groups.size > 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleFabAction() {
|
||||||
|
applyRunningState(isLoading = true, isRunning = false)
|
||||||
|
|
||||||
|
if (mainViewModel.isRunning.value == true) {
|
||||||
|
V2RayServiceManager.stopVService(this)
|
||||||
|
} else if ((MmkvManager.decodeSettingsString(AppConfig.PREF_MODE) ?: VPN) == VPN) {
|
||||||
|
val intent = VpnService.prepare(this)
|
||||||
|
if (intent == null) {
|
||||||
|
startV2Ray()
|
||||||
|
} else {
|
||||||
|
requestVpnPermission.launch(intent)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
startV2Ray()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleLayoutTestClick() {
|
||||||
|
if (mainViewModel.isRunning.value == true) {
|
||||||
|
setTestState(getString(R.string.connection_test_testing))
|
||||||
|
mainViewModel.testCurrentServerRealPing()
|
||||||
|
} else {
|
||||||
|
// service not running: keep existing no-op (could show a message if desired)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun startV2Ray() {
|
private fun startV2Ray() {
|
||||||
if (MmkvManager.getSelectServer().isNullOrEmpty()) {
|
if (MmkvManager.getSelectServer().isNullOrEmpty()) {
|
||||||
toast(R.string.title_file_chooser)
|
toast(R.string.title_file_chooser)
|
||||||
@@ -232,6 +227,31 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setTestState(content: String?) {
|
||||||
|
binding.tvTestState.text = content
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyRunningState(isLoading: Boolean, isRunning: Boolean) {
|
||||||
|
if (isLoading) {
|
||||||
|
binding.fab.setImageResource(R.drawable.ic_fab_check)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRunning) {
|
||||||
|
binding.fab.setImageResource(R.drawable.ic_stop_24dp)
|
||||||
|
binding.fab.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_fab_active))
|
||||||
|
binding.fab.contentDescription = getString(R.string.action_stop_service)
|
||||||
|
setTestState(getString(R.string.connection_connected))
|
||||||
|
binding.layoutTest.isFocusable = true
|
||||||
|
} else {
|
||||||
|
binding.fab.setImageResource(R.drawable.ic_play_24dp)
|
||||||
|
binding.fab.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_fab_inactive))
|
||||||
|
binding.fab.contentDescription = getString(R.string.tasker_start_service)
|
||||||
|
setTestState(getString(R.string.connection_not_connected))
|
||||||
|
binding.layoutTest.isFocusable = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
}
|
}
|
||||||
@@ -613,19 +633,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setTestState(content: String?) {
|
|
||||||
binding.tvTestState.text = content
|
|
||||||
}
|
|
||||||
|
|
||||||
// val mConnection = object : ServiceConnection {
|
|
||||||
// override fun onServiceDisconnected(name: ComponentName?) {
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
|
||||||
// sendMsg(AppConfig.MSG_REGISTER_CLIENT, "")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_BUTTON_B) {
|
if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_BUTTON_B) {
|
||||||
moveTaskToBack(false)
|
moveTaskToBack(false)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import com.v2ray.ang.helper.ItemTouchHelperAdapter
|
|||||||
import com.v2ray.ang.helper.ItemTouchHelperViewHolder
|
import com.v2ray.ang.helper.ItemTouchHelperViewHolder
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.Collections
|
||||||
|
|
||||||
class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<MainRecyclerAdapter.BaseViewHolder>(), ItemTouchHelperAdapter {
|
class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<MainRecyclerAdapter.BaseViewHolder>(), ItemTouchHelperAdapter {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -282,6 +283,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
|||||||
private fun removeServer(guid: String, position: Int) {
|
private fun removeServer(guid: String, position: Int) {
|
||||||
if (guid == MmkvManager.getSelectServer()) {
|
if (guid == MmkvManager.getSelectServer()) {
|
||||||
application.toast(R.string.toast_action_not_allowed)
|
application.toast(R.string.toast_action_not_allowed)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_CONFIRM_REMOVE)) {
|
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_CONFIRM_REMOVE)) {
|
||||||
@@ -368,6 +370,9 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
|
|||||||
|
|
||||||
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
|
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
|
||||||
mActivity.mainViewModel.swapServer(fromPosition, toPosition)
|
mActivity.mainViewModel.swapServer(fromPosition, toPosition)
|
||||||
|
if (fromPosition < data.size && toPosition < data.size) {
|
||||||
|
Collections.swap(data, fromPosition, toPosition)
|
||||||
|
}
|
||||||
notifyItemMoved(fromPosition, toPosition)
|
notifyItemMoved(fromPosition, toPosition)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,23 +21,18 @@ import com.v2ray.ang.util.Utils
|
|||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class SettingsActivity : BaseActivity() {
|
class SettingsActivity : BaseActivity() {
|
||||||
//private val settingsViewModel: SettingsViewModel by viewModels()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
//setContentView(R.layout.activity_settings)
|
|
||||||
setContentViewWithToolbar(R.layout.activity_settings, showHomeAsUp = true, title = getString(R.string.title_settings))
|
setContentViewWithToolbar(R.layout.activity_settings, showHomeAsUp = true, title = getString(R.string.title_settings))
|
||||||
|
|
||||||
//settingsViewModel.startListenPreferenceChange()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SettingsFragment : PreferenceFragmentCompat() {
|
class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
|
||||||
// private val perAppProxy by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_PER_APP_PROXY) }
|
|
||||||
private val localDns by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_LOCAL_DNS_ENABLED) }
|
private val localDns by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_LOCAL_DNS_ENABLED) }
|
||||||
private val fakeDns by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_FAKE_DNS_ENABLED) }
|
private val fakeDns by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_FAKE_DNS_ENABLED) }
|
||||||
private val appendHttpProxy by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_APPEND_HTTP_PROXY) }
|
private val appendHttpProxy by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_APPEND_HTTP_PROXY) }
|
||||||
// private val localDnsPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_LOCAL_DNS_PORT) }
|
|
||||||
|
// private val localDnsPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_LOCAL_DNS_PORT) }
|
||||||
private val vpnDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_VPN_DNS) }
|
private val vpnDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_VPN_DNS) }
|
||||||
private val vpnBypassLan by lazy { findPreference<ListPreference>(AppConfig.PREF_VPN_BYPASS_LAN) }
|
private val vpnBypassLan by lazy { findPreference<ListPreference>(AppConfig.PREF_VPN_BYPASS_LAN) }
|
||||||
private val vpnInterfaceAddress by lazy { findPreference<ListPreference>(AppConfig.PREF_VPN_INTERFACE_ADDRESS_CONFIG_INDEX) }
|
private val vpnInterfaceAddress by lazy { findPreference<ListPreference>(AppConfig.PREF_VPN_INTERFACE_ADDRESS_CONFIG_INDEX) }
|
||||||
@@ -55,19 +50,8 @@ class SettingsActivity : BaseActivity() {
|
|||||||
|
|
||||||
private val autoUpdateCheck by lazy { findPreference<CheckBoxPreference>(AppConfig.SUBSCRIPTION_AUTO_UPDATE) }
|
private val autoUpdateCheck by lazy { findPreference<CheckBoxPreference>(AppConfig.SUBSCRIPTION_AUTO_UPDATE) }
|
||||||
private val autoUpdateInterval by lazy { findPreference<EditTextPreference>(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL) }
|
private val autoUpdateInterval by lazy { findPreference<EditTextPreference>(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL) }
|
||||||
|
|
||||||
// private val socksPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_SOCKS_PORT) }
|
|
||||||
// private val remoteDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_REMOTE_DNS) }
|
|
||||||
// private val domesticDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DOMESTIC_DNS) }
|
|
||||||
// private val dnsHosts by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DNS_HOSTS) }
|
|
||||||
// private val delayTestUrl by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DELAY_TEST_URL) }
|
|
||||||
// private val ipApiUrl by lazy { findPreference<EditTextPreference>(AppConfig.PREF_IP_API_URL) }
|
|
||||||
private val mode by lazy { findPreference<ListPreference>(AppConfig.PREF_MODE) }
|
private val mode by lazy { findPreference<ListPreference>(AppConfig.PREF_MODE) }
|
||||||
|
|
||||||
// private val hevTunLogLevel by lazy { findPreference<ListPreference>(AppConfig.PREF_HEV_TUNNEL_LOGLEVEL) }
|
|
||||||
// private val hevTunRwTimeout by lazy { findPreference<EditTextPreference>(AppConfig.PREF_HEV_TUNNEL_RW_TIMEOUT) }
|
|
||||||
// private val useTun by lazy { findPreference<ListPreference>(AppConfig.PREF_TUN) }
|
|
||||||
|
|
||||||
override fun onCreatePreferences(bundle: Bundle?, s: String?) {
|
override fun onCreatePreferences(bundle: Bundle?, s: String?) {
|
||||||
// Use MMKV as the storage backend for all Preferences
|
// Use MMKV as the storage backend for all Preferences
|
||||||
// This prevents inconsistencies between SharedPreferences and MMKV
|
// This prevents inconsistencies between SharedPreferences and MMKV
|
||||||
@@ -77,30 +61,10 @@ class SettingsActivity : BaseActivity() {
|
|||||||
|
|
||||||
initPreferenceSummaries()
|
initPreferenceSummaries()
|
||||||
|
|
||||||
// perAppProxy?.setOnPreferenceClickListener {
|
|
||||||
// startActivity(Intent(activity, PerAppProxyActivity::class.java))
|
|
||||||
// perAppProxy?.isChecked = true
|
|
||||||
// false
|
|
||||||
// }
|
|
||||||
localDns?.setOnPreferenceChangeListener { _, any ->
|
localDns?.setOnPreferenceChangeListener { _, any ->
|
||||||
updateLocalDns(any as Boolean)
|
updateLocalDns(any as Boolean)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
// localDnsPort?.setOnPreferenceChangeListener { _, any ->
|
|
||||||
// val nval = any as String
|
|
||||||
// localDnsPort?.summary = nval.ifEmpty { AppConfig.PORT_LOCAL_DNS }
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
// vpnDns?.setOnPreferenceChangeListener { _, any ->
|
|
||||||
// vpnDns?.summary = any as String
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
|
|
||||||
// vpnMtu?.setOnPreferenceChangeListener { _, any ->
|
|
||||||
// val nval = any as String
|
|
||||||
// vpnMtu?.summary = nval.ifEmpty { AppConfig.VPN_MTU.toString() }
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
|
|
||||||
mux?.setOnPreferenceChangeListener { _, newValue ->
|
mux?.setOnPreferenceChangeListener { _, newValue ->
|
||||||
updateMux(newValue as Boolean)
|
updateMux(newValue as Boolean)
|
||||||
@@ -119,18 +83,6 @@ class SettingsActivity : BaseActivity() {
|
|||||||
updateFragment(newValue as Boolean)
|
updateFragment(newValue as Boolean)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
// fragmentPackets?.setOnPreferenceChangeListener { _, newValue ->
|
|
||||||
// updateFragmentPackets(newValue as String)
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
// fragmentLength?.setOnPreferenceChangeListener { _, newValue ->
|
|
||||||
// updateFragmentLength(newValue as String)
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
// fragmentInterval?.setOnPreferenceChangeListener { _, newValue ->
|
|
||||||
// updateFragmentInterval(newValue as String)
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
|
|
||||||
autoUpdateCheck?.setOnPreferenceChangeListener { _, newValue ->
|
autoUpdateCheck?.setOnPreferenceChangeListener { _, newValue ->
|
||||||
val value = newValue as Boolean
|
val value = newValue as Boolean
|
||||||
@@ -141,65 +93,12 @@ class SettingsActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
// autoUpdateInterval?.setOnPreferenceChangeListener { _, any ->
|
|
||||||
// var nval = any as String
|
|
||||||
//
|
|
||||||
// // It must be greater than 15 minutes because WorkManager couldn't run tasks under 15 minutes intervals
|
|
||||||
// nval =
|
|
||||||
// if (TextUtils.isEmpty(nval) || nval.toLongEx() < 15) AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL else nval
|
|
||||||
// autoUpdateInterval?.summary = nval
|
|
||||||
// configureUpdateTask(nval.toLongEx())
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
|
|
||||||
// socksPort?.setOnPreferenceChangeListener { _, any ->
|
|
||||||
// val nval = any as String
|
|
||||||
// socksPort?.summary = nval.ifEmpty { AppConfig.PORT_SOCKS }
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// remoteDns?.setOnPreferenceChangeListener { _, any ->
|
|
||||||
// val nval = any as String
|
|
||||||
// remoteDns?.summary = nval.ifEmpty { AppConfig.DNS_PROXY }
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
// domesticDns?.setOnPreferenceChangeListener { _, any ->
|
|
||||||
// val nval = any as String
|
|
||||||
// domesticDns?.summary = nval.ifEmpty { AppConfig.DNS_DIRECT }
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
// dnsHosts?.setOnPreferenceChangeListener { _, any ->
|
|
||||||
// val nval = any as String
|
|
||||||
// dnsHosts?.summary = nval
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
// delayTestUrl?.setOnPreferenceChangeListener { _, any ->
|
|
||||||
// val nval = any as String
|
|
||||||
// delayTestUrl?.summary = nval.ifEmpty { AppConfig.DELAY_TEST_URL }
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
// ipApiUrl?.setOnPreferenceChangeListener { _, any ->
|
|
||||||
// val nval = any as String
|
|
||||||
// ipApiUrl?.summary = nval.ifEmpty { AppConfig.IP_API_URL }
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
mode?.setOnPreferenceChangeListener { _, newValue ->
|
mode?.setOnPreferenceChangeListener { _, newValue ->
|
||||||
updateMode(newValue.toString())
|
updateMode(newValue.toString())
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
mode?.dialogLayoutResource = R.layout.preference_with_help_link
|
mode?.dialogLayoutResource = R.layout.preference_with_help_link
|
||||||
//loglevel.summary = "LogLevel"
|
|
||||||
|
|
||||||
// useTun?.setOnPreferenceChangeListener { _, newValue ->
|
|
||||||
// updateHevTunSettings(newValue as String == AppConfig.TUN_hevsocks5)
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
|
|
||||||
// hevTunRwTimeout?.setOnPreferenceChangeListener { _, any ->
|
|
||||||
// val nval = any as String
|
|
||||||
// hevTunRwTimeout?.summary = nval.ifEmpty { AppConfig.HEVTUN_RW_TIMEOUT }
|
|
||||||
// true
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initPreferenceSummaries() {
|
private fun initPreferenceSummaries() {
|
||||||
@@ -212,6 +111,7 @@ class SettingsActivity : BaseActivity() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ListPreference -> {
|
is ListPreference -> {
|
||||||
pref.summary = pref.entry ?: ""
|
pref.summary = pref.entry ?: ""
|
||||||
pref.setOnPreferenceChangeListener { p, newValue ->
|
pref.setOnPreferenceChangeListener { p, newValue ->
|
||||||
@@ -221,6 +121,7 @@ class SettingsActivity : BaseActivity() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is CheckBoxPreference, is androidx.preference.SwitchPreferenceCompat -> {
|
is CheckBoxPreference, is androidx.preference.SwitchPreferenceCompat -> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,100 +153,10 @@ class SettingsActivity : BaseActivity() {
|
|||||||
// Initialize auto-update interval state
|
// Initialize auto-update interval state
|
||||||
autoUpdateInterval?.isEnabled = MmkvManager.decodeSettingsBool(AppConfig.SUBSCRIPTION_AUTO_UPDATE, false)
|
autoUpdateInterval?.isEnabled = MmkvManager.decodeSettingsBool(AppConfig.SUBSCRIPTION_AUTO_UPDATE, false)
|
||||||
|
|
||||||
// localDns?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED, false)
|
|
||||||
// fakeDns?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_FAKE_DNS_ENABLED, false)
|
|
||||||
// appendHttpProxy?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_APPEND_HTTP_PROXY, false)
|
|
||||||
// vpnDns?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_DNS, AppConfig.DNS_VPN)
|
|
||||||
// vpnMtu?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_MTU, AppConfig.VPN_MTU.toString())
|
|
||||||
// mux?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_MUX_ENABLED, false)
|
|
||||||
// muxConcurrency?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_CONCURRENCY, "8")
|
|
||||||
// muxXudpConcurrency?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "8")
|
|
||||||
// fragment?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_FRAGMENT_ENABLED, false)
|
|
||||||
// fragmentPackets?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_PACKETS, "tlshello")
|
|
||||||
// fragmentLength?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_LENGTH, "50-100")
|
|
||||||
// fragmentInterval?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20")
|
|
||||||
// autoUpdateCheck?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.SUBSCRIPTION_AUTO_UPDATE, false)
|
|
||||||
// autoUpdateInterval?.summary =
|
|
||||||
// MmkvManager.decodeSettingsString(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL, AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL)
|
|
||||||
// socksPort?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_SOCKS_PORT, AppConfig.PORT_SOCKS)
|
|
||||||
// remoteDns?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_REMOTE_DNS, AppConfig.DNS_PROXY)
|
|
||||||
// domesticDns?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_DOMESTIC_DNS, AppConfig.DNS_DIRECT)
|
|
||||||
// dnsHosts?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_DNS_HOSTS)
|
|
||||||
// delayTestUrl?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_DELAY_TEST_URL, AppConfig.DELAY_TEST_URL)
|
|
||||||
// ipApiUrl?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_IP_API_URL, AppConfig.IP_API_URL)
|
|
||||||
// hevTunRwTimeout?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_HEV_TUNNEL_RW_TIMEOUT, AppConfig.HEVTUN_RW_TIMEOUT)
|
|
||||||
// updateHevTunSettings(MmkvManager.decodeSettingsString(AppConfig.PREF_TUN, AppConfig.TUN_hevsocks5) == AppConfig.TUN_hevsocks5)
|
|
||||||
|
|
||||||
// initSharedPreference()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initSharedPreference() {
|
|
||||||
// listOf(
|
|
||||||
// //localDnsPort,
|
|
||||||
// vpnDns,
|
|
||||||
// vpnMtu,
|
|
||||||
// muxConcurrency,
|
|
||||||
// muxXudpConcurrency,
|
|
||||||
// fragmentLength,
|
|
||||||
// fragmentInterval,
|
|
||||||
// autoUpdateInterval,
|
|
||||||
// socksPort,
|
|
||||||
// remoteDns,
|
|
||||||
// domesticDns,
|
|
||||||
// delayTestUrl,
|
|
||||||
// ipApiUrl,
|
|
||||||
// hevTunRwTimeout
|
|
||||||
// ).forEach { key ->
|
|
||||||
// key?.summary = key.text.toString()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// listOf(
|
|
||||||
// AppConfig.PREF_SNIFFING_ENABLED,
|
|
||||||
// AppConfig.PREF_USE_HEV_TUNNEL
|
|
||||||
// ).forEach { key ->
|
|
||||||
// findPreference<CheckBoxPreference>(key)?.isChecked =
|
|
||||||
// MmkvManager.decodeSettingsBool(key, true)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// listOf(
|
|
||||||
// AppConfig.PREF_ROUTE_ONLY_ENABLED,
|
|
||||||
// AppConfig.PREF_IS_BOOTED,
|
|
||||||
// AppConfig.PREF_BYPASS_APPS,
|
|
||||||
// AppConfig.PREF_SPEED_ENABLED,
|
|
||||||
// AppConfig.PREF_CONFIRM_REMOVE,
|
|
||||||
// AppConfig.PREF_START_SCAN_IMMEDIATE,
|
|
||||||
// AppConfig.PREF_DOUBLE_COLUMN_DISPLAY,
|
|
||||||
// AppConfig.PREF_PREFER_IPV6,
|
|
||||||
// AppConfig.PREF_PROXY_SHARING,
|
|
||||||
// AppConfig.PREF_ALLOW_INSECURE
|
|
||||||
// ).forEach { key ->
|
|
||||||
// findPreference<CheckBoxPreference>(key)?.isChecked =
|
|
||||||
// MmkvManager.decodeSettingsBool(key, false)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// listOf(
|
|
||||||
// AppConfig.PREF_VPN_BYPASS_LAN,
|
|
||||||
// AppConfig.PREF_VPN_INTERFACE_ADDRESS_CONFIG_INDEX,
|
|
||||||
// AppConfig.PREF_ROUTING_DOMAIN_STRATEGY,
|
|
||||||
// AppConfig.PREF_MUX_XUDP_QUIC,
|
|
||||||
// AppConfig.PREF_FRAGMENT_PACKETS,
|
|
||||||
// AppConfig.PREF_LANGUAGE,
|
|
||||||
// AppConfig.PREF_UI_MODE_NIGHT,
|
|
||||||
// AppConfig.PREF_LOGLEVEL,
|
|
||||||
// AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD,
|
|
||||||
// AppConfig.PREF_MODE,
|
|
||||||
// AppConfig.PREF_HEV_TUNNEL_LOGLEVEL
|
|
||||||
// ).forEach { key ->
|
|
||||||
// if (MmkvManager.decodeSettingsString(key) != null) {
|
|
||||||
// findPreference<ListPreference>(key)?.value = MmkvManager.decodeSettingsString(key)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateMode(mode: String?) {
|
private fun updateMode(mode: String?) {
|
||||||
val vpn = mode == VPN
|
val vpn = mode == VPN
|
||||||
// perAppProxy?.isEnabled = vpn
|
|
||||||
// perAppProxy?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_PER_APP_PROXY, false)
|
|
||||||
localDns?.isEnabled = vpn
|
localDns?.isEnabled = vpn
|
||||||
fakeDns?.isEnabled = vpn
|
fakeDns?.isEnabled = vpn
|
||||||
appendHttpProxy?.isEnabled = vpn
|
appendHttpProxy?.isEnabled = vpn
|
||||||
@@ -423,29 +234,7 @@ class SettingsActivity : BaseActivity() {
|
|||||||
fragmentPackets?.isEnabled = enabled
|
fragmentPackets?.isEnabled = enabled
|
||||||
fragmentLength?.isEnabled = enabled
|
fragmentLength?.isEnabled = enabled
|
||||||
fragmentInterval?.isEnabled = enabled
|
fragmentInterval?.isEnabled = enabled
|
||||||
// if (enabled) {
|
|
||||||
// updateFragmentPackets(MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_PACKETS, "tlshello"))
|
|
||||||
// updateFragmentLength(MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_LENGTH, "50-100"))
|
|
||||||
// updateFragmentInterval(MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20"))
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
//
|
|
||||||
// private fun updateFragmentPackets(value: String?) {
|
|
||||||
// fragmentPackets?.summary = value.toString()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private fun updateFragmentLength(value: String?) {
|
|
||||||
// fragmentLength?.summary = value.toString()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private fun updateFragmentInterval(value: String?) {
|
|
||||||
// fragmentInterval?.summary = value.toString()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private fun updateHevTunSettings(enabled: Boolean) {
|
|
||||||
// hevTunLogLevel?.isEnabled = enabled
|
|
||||||
// hevTunRwTimeout?.isEnabled = enabled
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onModeHelpClicked(view: View) {
|
fun onModeHelpClicked(view: View) {
|
||||||
|
|||||||
@@ -472,7 +472,10 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AppConfig.MSG_MEASURE_CONFIG_FINISH -> {
|
AppConfig.MSG_MEASURE_CONFIG_FINISH -> {
|
||||||
onTestsFinished()
|
val content = intent.getStringExtra("content")
|
||||||
|
if (content == "0") {
|
||||||
|
onTestsFinished()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user