From 5eae7befe18728120637130a3c8da4193d14c8ab Mon Sep 17 00:00:00 2001 From: Fasdeq13 Date: Mon, 9 Nov 2026 10:39:12 +0300 Subject: [PATCH 1/3] Update callbacks in TestPanelController Replaced onVisibilityChanged with onSorted callback to handle sorting events. --- .../io/nekohasekai/sagernet/ui/test/TestPanelController.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/test/TestPanelController.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/test/TestPanelController.kt index 30b67c5c4..612909353 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/ui/test/TestPanelController.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/ui/test/TestPanelController.kt @@ -79,8 +79,8 @@ class TestPanelController( /** Selects a profile like a tap in the list; by default the panel selects it and reloads a running service. */ var onSelectProfile: ((profileId: Long) -> Unit)? = null - /** Called when the panel starts to appear, and when it has gone (after sliding out). */ - var onVisibilityChanged: ((visible: Boolean) -> Unit)? = null + /** Called with the group and its fastest profile once a sort from the panel has been written. */ + var onSorted: ((groupId: Long, profileId: Long) -> Unit)? = null /** * The panel's height in pixels above the navigation bar while shown, 0 when hidden (e.g. to pad the list it From 96f53bd6ae4a91d8524776e280b29bb99e93bccf Mon Sep 17 00:00:00 2001 From: Fasdeq13 Date: Mon, 9 Nov 2026 10:39:58 +0300 Subject: [PATCH 2/3] Add onVisibilityChanged callback in TestPanelController Added a callback for visibility changes of the panel. --- .../io/nekohasekai/sagernet/ui/test/TestPanelController.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/test/TestPanelController.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/test/TestPanelController.kt index 612909353..fbb5764ba 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/ui/test/TestPanelController.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/ui/test/TestPanelController.kt @@ -82,6 +82,9 @@ class TestPanelController( /** Called with the group and its fastest profile once a sort from the panel has been written. */ var onSorted: ((groupId: Long, profileId: Long) -> Unit)? = null + /** Called when the panel starts to appear, and when it has gone (after sliding out). */ + var onVisibilityChanged: ((visible: Boolean) -> Unit)? = null + /** * The panel's height in pixels above the navigation bar while shown, 0 when hidden (e.g. to pad the list it * overlays). The panel reaches down to the bottom of [container]'s parent. Always 0 beside the list. @@ -844,6 +847,7 @@ class TestPanelController( if (groupId <= 0) return val bySpeed = s.kind == TestSpec.KIND_SPEED && !s.countryMode val upload = s.speedMode == TestUiState.SPEED_MODE_UPLOAD + val fastest = s.ranking.firstOrNull()?.profileId runOnDefaultDispatcher { val sorted = if (bySpeed) { // Kept like the latency sort, so the next stored result re-sorts instead of undoing the order. @@ -855,6 +859,7 @@ class TestPanelController( onMainDispatcher { if (fragment.isAdded) { fragment.snackbar(if (sorted) R.string.test_panel_sorted else R.string.test_panel_sort_busy).show() + if (sorted && fastest != null) onSorted?.invoke(groupId, fastest) } } } From 3ab58166ba8457a16dcb1fd1607af785703dd0c9 Mon Sep 17 00:00:00 2001 From: Fasdeq13 Date: Mon, 9 Nov 2026 10:40:33 +0300 Subject: [PATCH 3/3] Implement revealSorted method in ConfigurationFragment Added a method to reveal the sorted profile after sorting in the test panel. --- .../nekohasekai/sagernet/ui/ConfigurationFragment.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt index 7673ade66..4facfdf9b 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt @@ -303,6 +303,7 @@ class ConfigurationFragment : ToolbarFragment(R.layout.layout_group_list), if (!select) { testPanel = TestPanelController(this, panelContainer, side = sidePanel).apply { onSelectProfile = { id -> selectProfile(id, toggleOnTv = false) } + onSorted = { groupId, id -> revealSorted(groupId, id) } onHeightChanged = { height -> if (height != panelHeight) { panelHeight = height @@ -413,6 +414,16 @@ class ConfigurationFragment : ToolbarFragment(R.layout.layout_group_list), } } + /** After a sort from the test panel: brings the group's fastest profile into view once the new order is shown. */ + private fun revealSorted(groupId: Long, profileId: Long) { + val owner = viewLifecycleOwnerLiveData.value ?: return + owner.lifecycleScope.launch { + val list = listFor(groupId) ?: return@launch + list.adapter.reload()?.join() + if (query.isEmpty() && !selection.active) list.revealProfile(profileId) + } + } + /** The navigation icon in the toolbar's colours (the white theme draws it dark, as ToolbarFragment does). */ private fun setNavigationIcon(@DrawableRes icon: Int, @StringRes description: Int) { val toolbar = toolbar ?: return