mirror of
https://github.com/openlibrecommunity/olcng.git
synced 2026-07-03 14:05:17 +02:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d3a5a1af9c | |||
| 33971a576a | |||
| a04c53a045 | |||
| 278095015b |
@@ -164,6 +164,7 @@ object AppConfig {
|
||||
const val MSG_MEASURE_CONFIG_CANCEL = 72
|
||||
const val MSG_MEASURE_CONFIG_NOTIFY = 73
|
||||
const val MSG_MEASURE_CONFIG_FINISH = 74
|
||||
const val MSG_MEASURE_CONFIG_BATCH = 75
|
||||
|
||||
/** Notification channel IDs and names. */
|
||||
const val RAY_NG_CHANNEL_ID = "RAY_NG_M_CH_ID"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package xyz.zarazaex.olc.dto
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data class PingResultItem(
|
||||
val guid: String,
|
||||
val delay: Long
|
||||
) : Serializable
|
||||
|
||||
data class PingProgressUpdate(
|
||||
val results: ArrayList<PingResultItem>,
|
||||
val finished: Int,
|
||||
val total: Int
|
||||
) : Serializable
|
||||
@@ -128,4 +128,39 @@ data class ProfileItem(
|
||||
&& this.pinnedCA256 == obj.pinnedCA256
|
||||
)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = server?.hashCode() ?: 0
|
||||
result = 31 * result + (serverPort?.hashCode() ?: 0)
|
||||
result = 31 * result + (password?.hashCode() ?: 0)
|
||||
result = 31 * result + (method?.hashCode() ?: 0)
|
||||
result = 31 * result + (flow?.hashCode() ?: 0)
|
||||
result = 31 * result + (username?.hashCode() ?: 0)
|
||||
result = 31 * result + (network?.hashCode() ?: 0)
|
||||
result = 31 * result + (headerType?.hashCode() ?: 0)
|
||||
result = 31 * result + (host?.hashCode() ?: 0)
|
||||
result = 31 * result + (path?.hashCode() ?: 0)
|
||||
result = 31 * result + (seed?.hashCode() ?: 0)
|
||||
result = 31 * result + (quicSecurity?.hashCode() ?: 0)
|
||||
result = 31 * result + (quicKey?.hashCode() ?: 0)
|
||||
result = 31 * result + (mode?.hashCode() ?: 0)
|
||||
result = 31 * result + (serviceName?.hashCode() ?: 0)
|
||||
result = 31 * result + (authority?.hashCode() ?: 0)
|
||||
result = 31 * result + (xhttpMode?.hashCode() ?: 0)
|
||||
result = 31 * result + (security?.hashCode() ?: 0)
|
||||
result = 31 * result + (sni?.hashCode() ?: 0)
|
||||
result = 31 * result + (alpn?.hashCode() ?: 0)
|
||||
result = 31 * result + (fingerPrint?.hashCode() ?: 0)
|
||||
result = 31 * result + (publicKey?.hashCode() ?: 0)
|
||||
result = 31 * result + (shortId?.hashCode() ?: 0)
|
||||
result = 31 * result + (secretKey?.hashCode() ?: 0)
|
||||
result = 31 * result + (localAddress?.hashCode() ?: 0)
|
||||
result = 31 * result + (reserved?.hashCode() ?: 0)
|
||||
result = 31 * result + (mtu ?: 0)
|
||||
result = 31 * result + (obfsPassword?.hashCode() ?: 0)
|
||||
result = 31 * result + (portHopping?.hashCode() ?: 0)
|
||||
result = 31 * result + (portHoppingInterval?.hashCode() ?: 0)
|
||||
result = 31 * result + (pinnedCA256?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -241,8 +241,8 @@ object AngConfigManager {
|
||||
|
||||
val subItem = MmkvManager.decodeSubscription(subid)
|
||||
|
||||
val oldPingData = if (!append) {
|
||||
saveOldPingData(subid)
|
||||
val oldServerData = if (!append) {
|
||||
saveOldServerData(subid)
|
||||
} else {
|
||||
emptyMap()
|
||||
}
|
||||
@@ -263,7 +263,7 @@ object AngConfigManager {
|
||||
MmkvManager.removeServerViaSubid(subid)
|
||||
}
|
||||
val keyToProfile = batchSaveConfigs(configs, subid, append)
|
||||
restoreOldPingData(keyToProfile, oldPingData)
|
||||
restoreOldServerData(keyToProfile, oldServerData)
|
||||
val matchKey = findMatchedProfileKey(keyToProfile, removedSelected)
|
||||
matchKey?.let { MmkvManager.setSelectServer(it) }
|
||||
}
|
||||
@@ -330,31 +330,39 @@ object AngConfigManager {
|
||||
return keyToProfile
|
||||
}
|
||||
|
||||
private fun saveOldPingData(subid: String): Map<ProfileItem, Long> {
|
||||
val pingData = mutableMapOf<ProfileItem, Long>()
|
||||
private fun saveOldServerData(subid: String): Map<ProfileItem, Pair<Long, Boolean>> {
|
||||
val serverData = mutableMapOf<ProfileItem, Pair<Long, Boolean>>()
|
||||
val serverList = MmkvManager.decodeServerList(subid)
|
||||
|
||||
serverList.forEach { guid ->
|
||||
val profile = MmkvManager.decodeServerConfig(guid)
|
||||
val aff = MmkvManager.decodeServerAffiliationInfo(guid)
|
||||
if (profile != null && aff != null && aff.testDelayMillis > 0) {
|
||||
pingData[profile] = aff.testDelayMillis
|
||||
if (profile != null) {
|
||||
val aff = MmkvManager.decodeServerAffiliationInfo(guid)
|
||||
val delay = aff?.testDelayMillis ?: 0L
|
||||
if (delay > 0 || profile.isFavorite) {
|
||||
serverData[profile] = Pair(delay, profile.isFavorite)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pingData
|
||||
return serverData
|
||||
}
|
||||
|
||||
private fun restoreOldPingData(keyToProfile: Map<String, ProfileItem>, oldPingData: Map<ProfileItem, Long>) {
|
||||
if (oldPingData.isEmpty()) return
|
||||
private fun restoreOldServerData(keyToProfile: Map<String, ProfileItem>, oldServerData: Map<ProfileItem, Pair<Long, Boolean>>) {
|
||||
if (oldServerData.isEmpty()) return
|
||||
|
||||
keyToProfile.forEach { (key, newProfile) ->
|
||||
val oldPing = oldPingData.entries.firstOrNull { (oldProfile, _) ->
|
||||
oldProfile == newProfile
|
||||
}?.value
|
||||
val oldData = oldServerData[newProfile]
|
||||
|
||||
if (oldPing != null && oldPing > 0) {
|
||||
MmkvManager.encodeServerTestDelayMillis(key, oldPing)
|
||||
if (oldData != null) {
|
||||
val (oldPing, isFavorite) = oldData
|
||||
if (oldPing > 0) {
|
||||
MmkvManager.encodeServerTestDelayMillis(key, oldPing)
|
||||
}
|
||||
if (isFavorite) {
|
||||
newProfile.isFavorite = true
|
||||
MmkvManager.encodeServerConfig(key, newProfile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,12 @@ import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import xyz.zarazaex.olc.AppConfig
|
||||
import xyz.zarazaex.olc.dto.PingProgressUpdate
|
||||
import xyz.zarazaex.olc.dto.PingResultItem
|
||||
import xyz.zarazaex.olc.handler.SettingsManager
|
||||
import xyz.zarazaex.olc.handler.V2RayNativeManager
|
||||
import xyz.zarazaex.olc.handler.V2rayConfigManager
|
||||
@@ -29,12 +33,26 @@ class RealPingWorkerService(
|
||||
|
||||
private val totalCount = AtomicInteger(guids.size)
|
||||
private val finishedCount = AtomicInteger(0)
|
||||
private val pendingResults = ArrayList<PingResultItem>()
|
||||
private val pendingLock = Any()
|
||||
|
||||
private val delayTestUrl = SettingsManager.getDelayTestUrl()
|
||||
|
||||
companion object {
|
||||
private const val RESULT_BATCH_SIZE = 32
|
||||
private const val FLUSH_INTERVAL_MS = 1000L
|
||||
}
|
||||
|
||||
data class PingItem(val guid: String, val config: String)
|
||||
|
||||
fun start() {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
while (isActive) {
|
||||
delay(FLUSH_INTERVAL_MS)
|
||||
flushPendingResults()
|
||||
}
|
||||
}
|
||||
|
||||
scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
// Prepare configurations for batch test and shuffle for better async feel
|
||||
@@ -67,8 +85,10 @@ class RealPingWorkerService(
|
||||
)
|
||||
}
|
||||
|
||||
flushPendingResults()
|
||||
onFinish("0")
|
||||
} catch (e: Exception) {
|
||||
flushPendingResults()
|
||||
onFinish("-1")
|
||||
} finally {
|
||||
cancel()
|
||||
@@ -78,14 +98,42 @@ class RealPingWorkerService(
|
||||
|
||||
private fun reportResult(guid: String, delay: Long) {
|
||||
val finished = finishedCount.incrementAndGet()
|
||||
val total = guids.size
|
||||
var readyBatch: PingProgressUpdate? = null
|
||||
synchronized(pendingLock) {
|
||||
pendingResults.add(PingResultItem(guid, delay))
|
||||
if (pendingResults.size >= RESULT_BATCH_SIZE || finished >= totalCount.get()) {
|
||||
readyBatch = createProgressUpdateLocked(finished)
|
||||
pendingResults.clear()
|
||||
}
|
||||
}
|
||||
readyBatch?.let(::sendBatchUpdate)
|
||||
}
|
||||
|
||||
// Notify UI about the individual result
|
||||
MessageUtil.sendMsg2UI(context, AppConfig.MSG_MEASURE_CONFIG_SUCCESS, Pair(guid, delay))
|
||||
private fun flushPendingResults() {
|
||||
val finished = finishedCount.get()
|
||||
val update =
|
||||
synchronized(pendingLock) {
|
||||
if (pendingResults.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
createProgressUpdateLocked(finished).also { pendingResults.clear() }
|
||||
}
|
||||
}
|
||||
update?.let(::sendBatchUpdate)
|
||||
}
|
||||
|
||||
// Notify UI about progress
|
||||
val left = total - finished
|
||||
MessageUtil.sendMsg2UI(context, AppConfig.MSG_MEASURE_CONFIG_NOTIFY, "$left / $total")
|
||||
private fun createProgressUpdateLocked(finished: Int): PingProgressUpdate {
|
||||
return PingProgressUpdate(
|
||||
results = ArrayList(pendingResults),
|
||||
finished = finished,
|
||||
total = totalCount.get()
|
||||
)
|
||||
}
|
||||
|
||||
private fun sendBatchUpdate(update: PingProgressUpdate) {
|
||||
MessageUtil.sendMsg2UI(context, AppConfig.MSG_MEASURE_CONFIG_BATCH, update)
|
||||
val left = (update.total - update.finished).coerceAtLeast(0)
|
||||
MessageUtil.sendMsg2UI(context, AppConfig.MSG_MEASURE_CONFIG_NOTIFY, "$left / ${update.total}")
|
||||
}
|
||||
|
||||
fun cancel() {
|
||||
|
||||
@@ -17,6 +17,8 @@ import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
@@ -84,6 +86,12 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
toggle.syncState()
|
||||
binding.navView.setNavigationItemSelectedListener(this)
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.drawerContentLayout) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(0, systemBars.top, 0, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
findViewById<android.widget.TextView>(R.id.drawer_settings)?.setOnClickListener {
|
||||
requestActivityLauncher.launch(Intent(this, SettingsActivity::class.java))
|
||||
binding.drawerLayout.closeDrawer(androidx.core.view.GravityCompat.START)
|
||||
|
||||
@@ -15,6 +15,7 @@ import xyz.zarazaex.olc.AngApplication
|
||||
import xyz.zarazaex.olc.AppConfig
|
||||
import xyz.zarazaex.olc.R
|
||||
import xyz.zarazaex.olc.dto.GroupMapItem
|
||||
import xyz.zarazaex.olc.dto.PingProgressUpdate
|
||||
import xyz.zarazaex.olc.dto.ServersCache
|
||||
import xyz.zarazaex.olc.dto.SubscriptionCache
|
||||
import xyz.zarazaex.olc.dto.SubscriptionUpdateResult
|
||||
@@ -628,6 +629,14 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
updateListAction.value = getPosition(resultPair.first)
|
||||
}
|
||||
|
||||
AppConfig.MSG_MEASURE_CONFIG_BATCH -> {
|
||||
val update = intent.serializable<PingProgressUpdate>("content") ?: return
|
||||
update.results.forEach { result ->
|
||||
MmkvManager.encodeServerTestDelayMillis(result.guid, result.delay)
|
||||
}
|
||||
updateListAction.value = -1
|
||||
}
|
||||
|
||||
AppConfig.MSG_MEASURE_CONFIG_NOTIFY -> {
|
||||
val content = intent.getStringExtra("content")
|
||||
updateTestResultAction.value =
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/drawer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -150,6 +151,7 @@
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawer_content_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
|
||||
Reference in New Issue
Block a user