Skip to content

Commit 9fbc6c3

Browse files
tbobaja1ns
authored andcommitted
feat(Android): Add focused states on page transitions (software-mansion#1894)
## Description Currently, on Android TV devices when user is switching between views the last focused child is lost. This seems like an incorrect behavior - In software-mansion#1706 people are reporting that on Android TV the last focused element should be stored. This PR introduces the change to remember last focused element on previous screen. For now I've added a statement that the last element should be stored only for Android TV, but we should discuss if we also should store those elements for classic Android - I've noticed that it's possible to focus an element on a normal screen, but I've achieved it only by switching between elements with arrows on keyboard (so is it possible to focus an element just by touching a screen? Maybe TalkBack is also focusing the elements?). <details><summary> Focusing elements on Android </summary> https://github.com/software-mansion/react-native-screens/assets/23281839/4f62ae66-f411-4c45-b678-1e47c3fa4ff2 </details> Fixes software-mansion#1706. ## Changes - Added `isTelevision` method that checks if user is currently using Android TV. - Added `lastFocusedChild` variable with an implementation for finding last focused elements. ## Screenshots / GIFs Here you can add screenshots / GIFs documenting your change. You can add before / after section if you're changing some behavior. ### Before https://github.com/software-mansion/react-native-screens/assets/23281839/0e3e0ad4-a202-43c3-b688-6c66f76a43f8 ### After https://github.com/software-mansion/react-native-screens/assets/23281839/998dfa5c-6def-47f2-b129-425a2257319a ## Test code and steps to reproduce You can test those changes on `Example` application by switching between examples with arrows on the keyboard. ## Checklist - [ ] Ensured that CI passes
1 parent 3c2c559 commit 9fbc6c3

File tree

2 files changed

+37
-0
lines changed

2 files changed

+37
-0
lines changed

android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
1919
import com.facebook.react.uimanager.PixelUtil
2020
import com.google.android.material.appbar.AppBarLayout
2121
import com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior
22+
import com.swmansion.rnscreens.utils.DeviceUtils
2223

2324
class ScreenStackFragment : ScreenFragment, ScreenStackFragmentWrapper {
2425
private var mAppBarLayout: AppBarLayout? = null
2526
private var mToolbar: Toolbar? = null
2627
private var mShadowHidden = false
2728
private var mIsTranslucent = false
2829

30+
private var mLastFocusedChild: View? = null
31+
2932
var searchView: CustomSearchView? = null
3033
var onSearchViewCreate: ((searchView: CustomSearchView) -> Unit)? = null
3134

@@ -89,6 +92,11 @@ class ScreenStackFragment : ScreenFragment, ScreenStackFragmentWrapper {
8992
}
9093
}
9194

95+
override fun onStart() {
96+
mLastFocusedChild?.requestFocus()
97+
super.onStart()
98+
}
99+
92100
override fun onCreateView(
93101
inflater: LayoutInflater,
94102
container: ViewGroup?,
@@ -123,6 +131,13 @@ class ScreenStackFragment : ScreenFragment, ScreenStackFragmentWrapper {
123131
return view
124132
}
125133

134+
override fun onStop() {
135+
if (DeviceUtils.isPlatformAndroidTV(context))
136+
mLastFocusedChild = findLastFocusedChild()
137+
138+
super.onStop()
139+
}
140+
126141
override fun onPrepareOptionsMenu(menu: Menu) {
127142
updateToolbarMenu(menu)
128143
return super.onPrepareOptionsMenu(menu)
@@ -163,6 +178,16 @@ class ScreenStackFragment : ScreenFragment, ScreenStackFragmentWrapper {
163178
}
164179
}
165180

181+
private fun findLastFocusedChild(): View? {
182+
var view: View? = screen
183+
while (view != null) {
184+
if (view.isFocused) return view
185+
view = if (view is ViewGroup) view.focusedChild else null
186+
}
187+
188+
return null
189+
}
190+
166191
override fun canNavigateBack(): Boolean {
167192
val container: ScreenContainer? = screen.container
168193
check(container is ScreenStack) { "ScreenStackFragment added into a non-stack container" }
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.swmansion.rnscreens.utils
2+
3+
import android.content.Context
4+
import android.content.pm.PackageManager
5+
6+
object DeviceUtils {
7+
8+
fun isPlatformAndroidTV(context: Context?): Boolean {
9+
return context?.packageManager?.hasSystemFeature(PackageManager.FEATURE_LEANBACK) == true
10+
}
11+
12+
}

0 commit comments

Comments
 (0)