Skip to content

Commit

Permalink
Hide non-active destinations from screen readers (i.e. when a destina…
Browse files Browse the repository at this point in the history
…tion is pushed, but something is presented over the top of it, the pushed destination will be hidden from the screen reader)
  • Loading branch information
isaac-udy committed Nov 22, 2023
1 parent c148162 commit 54d6a7f
Showing 1 changed file with 61 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,51 @@ import androidx.compose.animation.core.Transition
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Box
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.ReusableContent
import androidx.compose.runtime.getValue
import androidx.compose.runtime.movableContentOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.platform.LocalSavedStateRegistryOwner
import androidx.lifecycle.*
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.invisibleToUser
import androidx.lifecycle.HasDefaultViewModelProviderFactory
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import androidx.savedstate.SavedStateRegistry
import androidx.savedstate.SavedStateRegistryOwner
import dev.enro.animation.NavigationAnimation
import dev.enro.core.*
import dev.enro.core.AnyOpenInstruction
import dev.enro.core.activity
import dev.enro.core.compose.ComposableDestination
import dev.enro.core.compose.LocalNavigationHandle
import dev.enro.core.compose.dialog.*
import dev.enro.core.compose.dialog.BottomSheetDestination
import dev.enro.core.compose.dialog.DialogDestination
import dev.enro.core.compose.dialog.EnroBottomSheetContainer
import dev.enro.core.compose.dialog.EnroDialogContainer
import dev.enro.core.container.NavigationContainer
import dev.enro.core.container.getAnimationsForEntering
import dev.enro.core.container.getAnimationsForExiting
import dev.enro.core.controller.usecase.ComposeEnvironment
import dev.enro.core.controller.usecase.OnNavigationContextCreated
import dev.enro.core.controller.usecase.OnNavigationContextSaved
import dev.enro.core.getNavigationHandle
import dev.enro.extensions.rememberLifecycleState

internal class ComposableDestinationOwner(
Expand Down Expand Up @@ -158,7 +183,7 @@ internal class ComposableDestinationOwner(
) return

ReusableContent(instruction.instructionId) {
ProvideRenderingWindow {
ProvideRenderingWindow(backstackState) {
animation.Animate(transition) {
renderDestination()
RegisterComposableLifecycleState(backstackState, parentContainer)
Expand Down Expand Up @@ -194,23 +219,40 @@ internal class ComposableDestinationOwner(
}
}

@OptIn(ExperimentalMaterialApi::class)
@OptIn(ExperimentalMaterialApi::class, ExperimentalComposeUiApi::class)
@Composable
private fun ProvideRenderingWindow(
content: @Composable () -> Unit
backstackState: List<AnyOpenInstruction>,
content: @Composable () -> Unit,
) {
when {
destination is DialogDestination -> EnroDialogContainer(
navigationHandle = destination.getNavigationHandle(),
destination = destination,
content = content
)
destination is BottomSheetDestination -> EnroBottomSheetContainer(
navigationHandle = destination.getNavigationHandle(),
destination = destination,
content = content
)
else -> content()
val isActive = remember(backstackState) {
backstackState.lastOrNull() == instruction
}
Box(
// When this ComposableDestinationOwner is not the current active item in the backstack, we're going to
// hide it from accessibility, so that you can't focus "through" presented destinations
modifier = Modifier.run {
when {
!isActive -> Modifier.clearAndSetSemantics { invisibleToUser() }
else -> this
}
}
) {
when {
destination is DialogDestination -> EnroDialogContainer(
navigationHandle = destination.getNavigationHandle(),
destination = destination,
content = content
)

destination is BottomSheetDestination -> EnroBottomSheetContainer(
navigationHandle = destination.getNavigationHandle(),
destination = destination,
content = content
)

else -> content()
}
}
}

Expand Down

0 comments on commit 54d6a7f

Please sign in to comment.