From d21101dc87a48d89eed0f5ec63a1f456e8a287dc Mon Sep 17 00:00:00 2001 From: coyaSONG <66289470+coyaSONG@users.noreply.github.com> Date: Wed, 15 Jul 2026 11:06:18 +0900 Subject: [PATCH 1/5] feat(search): focus input on navigation reselection --- .../main/java/com/lagradost/cloudstream3/MainActivity.kt | 6 ++++++ .../lagradost/cloudstream3/ui/search/SearchFragment.kt | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 5d39f6554cd..ebf326e914b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -1707,6 +1707,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa navController ) } + setOnItemReselectedListener { item -> + if (item.itemId == R.id.navigation_search) { + (navHostFragment.childFragmentManager.primaryNavigationFragment as? SearchFragment) + ?.focusSearchInput() + } + } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt index 5f5b064b543..bb73e06e18f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt @@ -81,6 +81,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.fixSystemBarsPadding import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard +import com.lagradost.cloudstream3.utils.UIHelper.showInputMethod import java.util.Locale import java.util.concurrent.locks.ReentrantLock @@ -157,6 +158,13 @@ class SearchFragment : BaseFragment( afterPluginsLoadedEvent -= ::reloadRepos } + internal fun focusSearchInput() { + binding?.mainSearch?.apply { + requestFocus() + post { showInputMethod(findFocus()) } + } + } + var selectedSearchTypes = mutableListOf() var selectedApis = mutableSetOf() From 8baa4231fde8c58a61ccbb39226b9e37ce5788ff Mon Sep 17 00:00:00 2001 From: coyaSONG <66289470+coyaSONG@users.noreply.github.com> Date: Wed, 15 Jul 2026 17:12:38 +0900 Subject: [PATCH 2/5] refactor(search): reuse navigation selection path --- .../lagradost/cloudstream3/MainActivity.kt | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index ebf326e914b..99c5f189d37 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -746,7 +746,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa hierarchy.any { it.id == destId } private var lastNavTime = 0L - private fun onNavDestinationSelected(item: MenuItem, navController: NavController): Boolean { + private fun onNavDestinationSelected( + item: MenuItem, + navController: NavController, + navHostFragment: NavHostFragment, + ): Boolean { val currentTime = System.currentTimeMillis() // safeDebounce: Check if a previous tap happened within the last 400ms if (currentTime - lastNavTime < 400) return false @@ -755,7 +759,13 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa val destinationId = item.itemId // Check if we are already at the selected destination - if (navController.currentDestination?.id == destinationId) return false + if (navController.currentDestination?.id == destinationId) { + if (destinationId == R.id.navigation_search) { + (navHostFragment.childFragmentManager.primaryNavigationFragment as? SearchFragment) + ?.focusSearchInput() + } + return false + } // Make all nav buttons focus on this specific view when nextFocusRightId val targetView = when (destinationId) { @@ -1704,15 +1714,10 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa setOnItemSelectedListener { item -> onNavDestinationSelected( item, - navController + navController, + navHostFragment ) } - setOnItemReselectedListener { item -> - if (item.itemId == R.id.navigation_search) { - (navHostFragment.childFragmentManager.primaryNavigationFragment as? SearchFragment) - ?.focusSearchInput() - } - } } @@ -1738,7 +1743,8 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa setOnItemSelectedListener { item -> onNavDestinationSelected( item, - navController + navController, + navHostFragment ) } From cbff3833dc27eaff1836fe4d4087b17a2c84bd71 Mon Sep 17 00:00:00 2001 From: coyaSONG <66289470+coyaSONG@users.noreply.github.com> Date: Wed, 15 Jul 2026 19:14:25 +0900 Subject: [PATCH 3/5] fix(search): request focus from touch --- .../java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt index bb73e06e18f..bfd9724c88c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt @@ -160,7 +160,7 @@ class SearchFragment : BaseFragment( internal fun focusSearchInput() { binding?.mainSearch?.apply { - requestFocus() + requestFocusFromTouch() post { showInputMethod(findFocus()) } } } From 6b2cc659bb343dfa6a5a4900278f3894c31c6acf Mon Sep 17 00:00:00 2001 From: coyaSONG <66289470+coyaSONG@users.noreply.github.com> Date: Wed, 15 Jul 2026 22:58:35 +0900 Subject: [PATCH 4/5] fix(search): rely on requestFocusFromTouch --- .../com/lagradost/cloudstream3/ui/search/SearchFragment.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt index bfd9724c88c..644e2c6c457 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt @@ -81,7 +81,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.fixSystemBarsPadding import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard -import com.lagradost.cloudstream3.utils.UIHelper.showInputMethod import java.util.Locale import java.util.concurrent.locks.ReentrantLock @@ -159,10 +158,7 @@ class SearchFragment : BaseFragment( } internal fun focusSearchInput() { - binding?.mainSearch?.apply { - requestFocusFromTouch() - post { showInputMethod(findFocus()) } - } + binding?.mainSearch?.requestFocusFromTouch() } var selectedSearchTypes = mutableListOf() From 909a00c08b3ffd40a506f67f6c3cb9edba8e87d0 Mon Sep 17 00:00:00 2001 From: coyaSONG Date: Sun, 30 Aug 2026 04:18:40 +0900 Subject: [PATCH 5/5] fix(search): limit reselection focus to phone layouts --- .../lagradost/cloudstream3/MainActivity.kt | 20 ++++++-- .../cloudstream3/SearchNavigationTest.kt | 51 +++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 app/src/test/java/com/lagradost/cloudstream3/SearchNavigationTest.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 99c5f189d37..d84005d0afa 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -194,6 +194,15 @@ import kotlin.math.absoluteValue import kotlin.reflect.full.createInstance import kotlin.system.exitProcess +internal fun shouldFocusSearchOnReselection( + currentDestinationId: Int?, + selectedDestinationId: Int, + isPhoneLayout: Boolean, +): Boolean = + isPhoneLayout && + currentDestinationId == R.id.navigation_search && + selectedDestinationId == R.id.navigation_search + class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCallback { companion object { var activityResultLauncher: ActivityResultLauncher? = null @@ -759,11 +768,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa val destinationId = item.itemId // Check if we are already at the selected destination - if (navController.currentDestination?.id == destinationId) { - if (destinationId == R.id.navigation_search) { - (navHostFragment.childFragmentManager.primaryNavigationFragment as? SearchFragment) - ?.focusSearchInput() - } + val currentDestinationId = navController.currentDestination?.id + if (shouldFocusSearchOnReselection(currentDestinationId, destinationId, isLayout(PHONE))) { + (navHostFragment.childFragmentManager.primaryNavigationFragment as? SearchFragment) + ?.focusSearchInput() + } + if (currentDestinationId == destinationId) { return false } diff --git a/app/src/test/java/com/lagradost/cloudstream3/SearchNavigationTest.kt b/app/src/test/java/com/lagradost/cloudstream3/SearchNavigationTest.kt new file mode 100644 index 00000000000..6d62a083342 --- /dev/null +++ b/app/src/test/java/com/lagradost/cloudstream3/SearchNavigationTest.kt @@ -0,0 +1,51 @@ +package com.lagradost.cloudstream3 + +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class SearchNavigationTest { + @Test + fun `phone search reselection requests focus`() { + assertTrue( + shouldFocusSearchOnReselection( + currentDestinationId = R.id.navigation_search, + selectedDestinationId = R.id.navigation_search, + isPhoneLayout = true, + ) + ) + } + + @Test + fun `tv search reselection keeps existing focus behavior`() { + assertFalse( + shouldFocusSearchOnReselection( + currentDestinationId = R.id.navigation_search, + selectedDestinationId = R.id.navigation_search, + isPhoneLayout = false, + ) + ) + } + + @Test + fun `first search selection does not request focus`() { + assertFalse( + shouldFocusSearchOnReselection( + currentDestinationId = R.id.navigation_home, + selectedDestinationId = R.id.navigation_search, + isPhoneLayout = true, + ) + ) + } + + @Test + fun `non-search reselection does not request focus`() { + assertFalse( + shouldFocusSearchOnReselection( + currentDestinationId = R.id.navigation_home, + selectedDestinationId = R.id.navigation_home, + isPhoneLayout = true, + ) + ) + } +}