2 Commits

Author SHA1 Message Date
zarazaex69 d3a5a1af9c fix: preserve favorite status during subscription update 2026-04-21 23:07:51 +03:00
zarazaex69 33971a576a fix: #7 2026-04-21 23:00:36 +03:00
4 changed files with 70 additions and 17 deletions
@@ -128,4 +128,39 @@ data class ProfileItem(
&& this.pinnedCA256 == obj.pinnedCA256 && 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 subItem = MmkvManager.decodeSubscription(subid)
val oldPingData = if (!append) { val oldServerData = if (!append) {
saveOldPingData(subid) saveOldServerData(subid)
} else { } else {
emptyMap() emptyMap()
} }
@@ -263,7 +263,7 @@ object AngConfigManager {
MmkvManager.removeServerViaSubid(subid) MmkvManager.removeServerViaSubid(subid)
} }
val keyToProfile = batchSaveConfigs(configs, subid, append) val keyToProfile = batchSaveConfigs(configs, subid, append)
restoreOldPingData(keyToProfile, oldPingData) restoreOldServerData(keyToProfile, oldServerData)
val matchKey = findMatchedProfileKey(keyToProfile, removedSelected) val matchKey = findMatchedProfileKey(keyToProfile, removedSelected)
matchKey?.let { MmkvManager.setSelectServer(it) } matchKey?.let { MmkvManager.setSelectServer(it) }
} }
@@ -330,32 +330,40 @@ object AngConfigManager {
return keyToProfile return keyToProfile
} }
private fun saveOldPingData(subid: String): Map<ProfileItem, Long> { private fun saveOldServerData(subid: String): Map<ProfileItem, Pair<Long, Boolean>> {
val pingData = mutableMapOf<ProfileItem, Long>() val serverData = mutableMapOf<ProfileItem, Pair<Long, Boolean>>()
val serverList = MmkvManager.decodeServerList(subid) val serverList = MmkvManager.decodeServerList(subid)
serverList.forEach { guid -> serverList.forEach { guid ->
val profile = MmkvManager.decodeServerConfig(guid) val profile = MmkvManager.decodeServerConfig(guid)
if (profile != null) {
val aff = MmkvManager.decodeServerAffiliationInfo(guid) val aff = MmkvManager.decodeServerAffiliationInfo(guid)
if (profile != null && aff != null && aff.testDelayMillis > 0) { val delay = aff?.testDelayMillis ?: 0L
pingData[profile] = aff.testDelayMillis 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>) { private fun restoreOldServerData(keyToProfile: Map<String, ProfileItem>, oldServerData: Map<ProfileItem, Pair<Long, Boolean>>) {
if (oldPingData.isEmpty()) return if (oldServerData.isEmpty()) return
keyToProfile.forEach { (key, newProfile) -> keyToProfile.forEach { (key, newProfile) ->
val oldPing = oldPingData.entries.firstOrNull { (oldProfile, _) -> val oldData = oldServerData[newProfile]
oldProfile == newProfile
}?.value
if (oldPing != null && oldPing > 0) { if (oldData != null) {
val (oldPing, isFavorite) = oldData
if (oldPing > 0) {
MmkvManager.encodeServerTestDelayMillis(key, oldPing) MmkvManager.encodeServerTestDelayMillis(key, oldPing)
} }
if (isFavorite) {
newProfile.isFavorite = true
MmkvManager.encodeServerConfig(key, newProfile)
}
}
} }
} }
@@ -17,6 +17,8 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
@@ -84,6 +86,12 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
toggle.syncState() toggle.syncState()
binding.navView.setNavigationItemSelectedListener(this) 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 { findViewById<android.widget.TextView>(R.id.drawer_settings)?.setOnClickListener {
requestActivityLauncher.launch(Intent(this, SettingsActivity::class.java)) requestActivityLauncher.launch(Intent(this, SettingsActivity::class.java))
binding.drawerLayout.closeDrawer(androidx.core.view.GravityCompat.START) binding.drawerLayout.closeDrawer(androidx.core.view.GravityCompat.START)
@@ -3,7 +3,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout" android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:fitsSystemWindows="true">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -150,6 +151,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/drawer_content_layout"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="start" android:layout_gravity="start"