Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions android/src/main/java/com/swmansion/rnscreens/Screen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.swmansion.rnscreens
import android.annotation.SuppressLint
import android.content.pm.ActivityInfo
import android.graphics.Paint
import android.os.Build
import android.os.Parcelable
import android.util.SparseArray
import android.view.MotionEvent
Expand All @@ -11,6 +12,7 @@ import android.view.ViewGroup
import android.view.WindowManager
import android.webkit.WebView
import android.widget.ImageView
import androidx.annotation.RequiresApi
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.children
import androidx.fragment.app.Fragment
Expand Down Expand Up @@ -345,6 +347,35 @@ class Screen(
this.headerConfig?.toolbar?.importantForAccessibility = mode
}

// Accepts one of 3 focusable flags and one of 3 descendantFocusability flags.
// https://developer.android.com/reference/android/view/View#setFocusable(int)
// https://developer.android.com/reference/android/view/ViewGroup#setDescendantFocusability(int)
@RequiresApi(Build.VERSION_CODES.O)
fun changeFocusability(
focusable: Int,
descendantFocusability: Int,
) {
this.focusable = focusable
this.headerConfig?.toolbar?.focusable = focusable

this.descendantFocusability = descendantFocusability
this.headerConfig?.toolbar?.descendantFocusability = descendantFocusability
}

// Accepts boolean for focusability and one of 3 descendantFocusability flags.
// This method is provided for compatibility reasons only (SDK_API <= 25).
// https://developer.android.com/reference/android/view/ViewGroup#setDescendantFocusability(int)
fun changeFocusabilityCompat(
focusable: Boolean,
descendantFocusability: Int,
) {
this.setFocusable(focusable)
this.headerConfig?.toolbar?.setFocusable(focusable)

this.descendantFocusability = descendantFocusability
this.headerConfig?.toolbar?.descendantFocusability = descendantFocusability
}

var statusBarStyle: String? = null
set(statusBarStyle) {
if (statusBarStyle != null) {
Expand Down
27 changes: 27 additions & 0 deletions android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ class ScreenStack(
transaction.add(id, newTop.fragment)
}

val previousTopScreen: Screen? = topScreenWrapper?.screen
topScreenWrapper = newTop as? ScreenStackFragmentWrapper
stack.clear()
stack.addAll(screenWrappers.asSequence().map { it as ScreenStackFragmentWrapper })
Expand All @@ -279,6 +280,13 @@ class ScreenStack(
.toList()

turnOffA11yUnderTransparentScreen(visibleBottom)

// When form sheet is pushed to the top of the stack, we need to make sure that keyboard
// focus doesn't stay on the previous screen. We don't use requestFocus on newTop's
// screen because if there is an input field in the form sheet, it works as if autoFocus
// was enabled on the input field instead of focusing e.g. button above the input.
previousTopScreen?.clearFocus()

transaction.commitNowAllowingStateLoss()
}
}
Expand All @@ -296,6 +304,20 @@ class ScreenStack(
IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS,
)

// Keyboard navigation focus is separate from screen reader focus, that's
// why we need to use focusable and descendantFocusability.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
fragmentWrapper.screen.changeFocusability(
NOT_FOCUSABLE,
FOCUS_BLOCK_DESCENDANTS,
)
} else {
fragmentWrapper.screen.changeFocusabilityCompat(
false,
FOCUS_BLOCK_DESCENDANTS,
)
}

// don't change a11y below non-transparent screens
if (fragmentWrapper == visibleBottom) {
break
Expand All @@ -306,6 +328,11 @@ class ScreenStack(
}

topScreen?.changeAccessibilityMode(IMPORTANT_FOR_ACCESSIBILITY_AUTO)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
topScreen?.changeFocusability(FOCUSABLE_AUTO, FOCUS_BEFORE_DESCENDANTS)
} else {
topScreen?.changeFocusabilityCompat(true, FOCUS_BEFORE_DESCENDANTS)
}
}

override fun notifyContainerUpdate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ internal class DimmingView(

init {
pointerEventsProxy.pointerEventsImpl = DimmingViewPointerEventsImpl(this)
setFocusable(false)
}

internal val blockGestures
Expand Down
Loading