Skip to content

Commit cd45b51

Browse files
committed
Add locate FAB and scroll-to-selected server
#5165
1 parent 6aa26d2 commit cd45b51

File tree

14 files changed

+119
-1
lines changed

14 files changed

+119
-1
lines changed

V2rayNG/app/src/main/java/com/v2ray/ang/ui/GroupServerFragment.kt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,38 @@ class GroupServerFragment : BaseFragment<FragmentGroupServerBinding>(),
280280
ownerActivity.importConfigViaSub()
281281
binding.refreshLayout.isRefreshing = false
282282
}
283+
284+
/**
285+
* Scrolls to the currently selected server in the RecyclerView
286+
*/
287+
fun scrollToSelectedServer() {
288+
val selectedGuid = MmkvManager.getSelectServer()
289+
if (selectedGuid.isNullOrEmpty()) {
290+
ownerActivity.toast(R.string.title_file_chooser)
291+
return
292+
}
293+
294+
// Find the position of the selected server
295+
val serversCache = mainViewModel.serversCache
296+
val position = serversCache.indexOfFirst { it.guid == selectedGuid }
297+
val recyclerView = binding.recyclerView
298+
299+
if (position >= 0) {
300+
// Get the layout manager
301+
val layoutManager = recyclerView.layoutManager as? GridLayoutManager
302+
303+
if (layoutManager != null) {
304+
// Scroll to position with offset to center it on screen
305+
// First scroll to position, then adjust to center
306+
recyclerView.post {
307+
layoutManager.scrollToPositionWithOffset(position, recyclerView.height / 3)
308+
}
309+
} else {
310+
// Fallback to smooth scroll if layout manager is not GridLayoutManager
311+
recyclerView.smoothScrollToPosition(position)
312+
}
313+
} else {
314+
ownerActivity.toast(R.string.toast_server_not_found_in_group)
315+
}
316+
}
283317
}

V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
9494
})
9595

9696
binding.fab.setOnClickListener { handleFabAction() }
97+
binding.fabLocate.setOnClickListener { locateSelectedServer() }
9798
binding.layoutTest.setOnClickListener { handleLayoutTestClick() }
9899

99100
setupGroupTab()
@@ -569,6 +570,47 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
569570
}
570571
}
571572

573+
/**
574+
* Locates and scrolls to the currently selected server.
575+
* If the selected server is in a different group, automatically switches to that group first.
576+
*/
577+
private fun locateSelectedServer() {
578+
val targetSubscriptionId = mainViewModel.findSubscriptionIdBySelect()
579+
if (targetSubscriptionId.isNullOrEmpty()) {
580+
toast(R.string.title_file_chooser)
581+
return
582+
}
583+
584+
val targetGroupIndex = groupPagerAdapter.groups.indexOfFirst { it.id == targetSubscriptionId }
585+
if (targetGroupIndex < 0) {
586+
toast(R.string.toast_server_not_found_in_group)
587+
return
588+
}
589+
590+
// Switch to target group if needed, then scroll to the server
591+
if (binding.viewPager.currentItem != targetGroupIndex) {
592+
binding.viewPager.setCurrentItem(targetGroupIndex, true)
593+
binding.viewPager.postDelayed({ scrollToSelectedServer(targetGroupIndex) }, 1000)
594+
} else {
595+
scrollToSelectedServer(targetGroupIndex)
596+
}
597+
}
598+
599+
/**
600+
* Scrolls to the selected server in the specified fragment.
601+
* @param groupIndex The index of the group/fragment to scroll in
602+
*/
603+
private fun scrollToSelectedServer(groupIndex: Int) {
604+
val itemId = groupPagerAdapter.getItemId(groupIndex)
605+
val fragment = supportFragmentManager.findFragmentByTag("f$itemId") as? GroupServerFragment
606+
607+
if (fragment?.isAdded == true && fragment.view != null) {
608+
fragment.scrollToSelectedServer()
609+
} else {
610+
toast(R.string.toast_fragment_not_available)
611+
}
612+
}
613+
572614
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
573615
if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_BUTTON_B) {
574616
moveTaskToBack(false)

V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,17 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
402402
reloadServerList()
403403
}
404404

405+
fun findSubscriptionIdBySelect(): String? {
406+
// Get the selected server GUID
407+
val selectedGuid = MmkvManager.getSelectServer()
408+
if (selectedGuid.isNullOrEmpty()) {
409+
return null
410+
}
411+
412+
val config = MmkvManager.decodeServerConfig(selectedGuid)
413+
return config?.subscriptionId
414+
}
415+
405416
fun onTestsFinished() {
406417
viewModelScope.launch(Dispatchers.Default) {
407418
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_AUTO_REMOVE_INVALID_AFTER_TEST)) {
@@ -468,4 +479,4 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
468479
}
469480
}
470481
}
471-
}
482+
}

V2rayNG/app/src/main/res/layout/activity_main.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,18 @@
9696
android:layout_gravity="bottom|end"
9797
android:layout_marginEnd="@dimen/padding_spacing_dp16">
9898

99+
<com.google.android.material.floatingactionbutton.FloatingActionButton
100+
android:id="@+id/fab_locate"
101+
android:layout_width="wrap_content"
102+
android:layout_height="wrap_content"
103+
android:layout_gravity="bottom|end"
104+
android:layout_marginBottom="@dimen/view_height_dp120"
105+
android:clickable="true"
106+
android:contentDescription="@string/title_server"
107+
android:focusable="true"
108+
android:src="@android:drawable/ic_menu_mylocation"
109+
app:layout_anchorGravity="bottom|right|end" />
110+
99111
<com.google.android.material.floatingactionbutton.FloatingActionButton
100112
android:id="@+id/fab"
101113
android:layout_width="wrap_content"

V2rayNG/app/src/main/res/values-ar/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@
299299
<string name="title_update_config_count">Update %d configurations</string>
300300
<string name="title_update_subscription_result">Updated %1$d configs (%2$d success, %3$d failed, %4$d skipped)</string>
301301
<string name="title_update_subscription_no_subscription">No subscriptions</string>
302+
<string name="toast_server_not_found_in_group">Selected server not found in current group</string>
303+
<string name="toast_fragment_not_available">Unable to locate current view</string>
302304
<string name="tasker_start_service">بدء الخدمة</string>
303305
<string name="tasker_setting_confirm">تأكيد</string>
304306

V2rayNG/app/src/main/res/values-bn/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@
298298
<string name="title_update_config_count">Update %d configurations</string>
299299
<string name="title_update_subscription_result">Updated %1$d configs (%2$d success, %3$d failed, %4$d skipped)</string>
300300
<string name="title_update_subscription_no_subscription">No subscriptions</string>
301+
<string name="toast_server_not_found_in_group">Selected server not found in current group</string>
302+
<string name="toast_fragment_not_available">Unable to locate current view</string>
301303
<string name="tasker_start_service">সার্ভিস শুরু করুন</string>
302304
<string name="tasker_setting_confirm">নিশ্চিত করুন</string>
303305

V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@
299299
<string name="title_update_config_count">ورۊ کردن %d کانفیگ</string>
300300
<string name="title_update_subscription_result">%1$d کانفیگ ورۊ وابی (مووفق: %2$d، شکست خرده: %3$d، رڌ وابیڌه: %4$d)</string>
301301
<string name="title_update_subscription_no_subscription">بؽ اشتراک</string>
302+
<string name="toast_server_not_found_in_group">Selected server not found in current group</string>
303+
<string name="toast_fragment_not_available">Unable to locate current view</string>
302304
<string name="tasker_start_service">ره وندن خدمات</string>
303305
<string name="tasker_setting_confirm">قوۊل</string>
304306

V2rayNG/app/src/main/res/values-fa/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,8 @@
296296
<string name="title_update_config_count">آپدیت کردن %d کانفیگ</string>
297297
<string name="title_update_subscription_result">Updated %1$d configs (%2$d success, %3$d failed, %4$d skipped)</string>
298298
<string name="title_update_subscription_no_subscription">No subscriptions</string>
299+
<string name="toast_server_not_found_in_group">Selected server not found in current group</string>
300+
<string name="toast_fragment_not_available">Unable to locate current view</string>
299301
<string name="tasker_start_service">شروع خدمات</string>
300302
<string name="tasker_setting_confirm">تایید</string>
301303

V2rayNG/app/src/main/res/values-ru/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,8 @@
297297
<string name="title_update_config_count">Обновлено профилей: %d</string>
298298
<string name="title_update_subscription_result">Обновлено профилей: %1$d (успешно: %2$d, ошибок: %3$d, пропущено: %4$d)</string>
299299
<string name="title_update_subscription_no_subscription">Нет подписок</string>
300+
<string name="toast_server_not_found_in_group">Selected server not found in current group</string>
301+
<string name="toast_fragment_not_available">Unable to locate current view</string>
300302

301303
<string name="tasker_start_service">Запуск службы</string>
302304
<string name="tasker_setting_confirm">Подтвердить</string>

V2rayNG/app/src/main/res/values-vi/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@
299299
<string name="title_update_config_count">Update %d configurations</string>
300300
<string name="title_update_subscription_result">Updated %1$d configs (%2$d success, %3$d failed, %4$d skipped)</string>
301301
<string name="title_update_subscription_no_subscription">No subscriptions</string>
302+
<string name="toast_server_not_found_in_group">Selected server not found in current group</string>
303+
<string name="toast_fragment_not_available">Unable to locate current view</string>
302304
<string name="tasker_start_service">Khởi động v2rayNG</string>
303305
<string name="tasker_setting_confirm">Xác nhận</string>
304306

0 commit comments

Comments
 (0)