Skip to content

Commit

Permalink
Updated NavigationContainer handling to not always accept presented i…
Browse files Browse the repository at this point in the history
…nstructions
  • Loading branch information
isaac-udy committed Sep 12, 2024
1 parent 5c4a970 commit 0ff64ba
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 6 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
* ⚠️ Updated result channel identifiers in preparation for Kotlin 2.0 ⚠️
* Kotlin 2.0 changes the way that lambdas are compiled, which has implications for `registerForNavigationResult` and how result channels are uniquely identified. Activites, Fragments, Composables and ViewModels that use `by registerForNavigationResult` directly will not be affected by this change. However, if you are creating result channels inside of other objects, such as delegates, helper objects, or extension functions, you should verify that these cases continue to work as expected. It is not expected that there will be issues, but if this does result in bugs in your application, please raise them on the Enro GitHub repository.

* ⚠️ Updated NavigationContainer handling of NavigationInstructionFilter ⚠️
* In versions of Enro before 2.8.0, NavigationContainers would always accept destinations that were presented (`NavigationInstruction.Present(...)`, `navigationHandle.present(...)`, etc), and would only enforce their instructionFilter for pushed instructions (`NavigationInstruction.Push(...)`, `navigationHandle.push(...)`, etc). This is no longer the default behavior, and NavigationContainers will apply their instructionFilter to all instructions.
* This behavior can be reverted to the previous behavior by setting `useLegacyContainerPresentBehavior` when creating a NavigationController for your application using `createNavigationController`.
* `useLegacyContainerPresentBehavior` will be removed in a future version of Enro, and it is recommended that you update your NavigationContainers to explicitly declare their instructionFilter for all instructions, not just pushed instructions.

## 2.7.0
* ⚠️ Updated to androidx.lifecycle 2.8.1 ⚠️
* There are breaking changes introduced in androidx.lifecycle 2.8.0; if you use Enro 2.7.0, you must upgrade your project to androidx.lifecycle 2.8+, otherwise you are likely to encounter runtime errors
Expand Down
9 changes: 8 additions & 1 deletion enro-core/src/main/java/dev/enro/core/EnroConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@ package dev.enro.core
import dev.enro.core.controller.EnroBackConfiguration

public data class EnroConfig(
val isInTest: Boolean = false,
internal val isInTest: Boolean = false,
internal val isAnimationsDisabled: Boolean = false,
internal val isStrictMode: Boolean = false,
/**
* In versions of Enro before 2.8.0, NavigationContainers would always accept destinations that were presented, and
* would only enforce their navigation instruction filter for pushed instructions. This is no longer the default
* behavior, but can be re-enabled by setting this Boolean to true.
*/
@Deprecated("This behavior is no longer recommended, and will be removed in a future version of Enro. Please update your NavigationContainers to use a NavigationInstructionFilter that explicitly declares all instructions that are valid for the container.")
internal val useLegacyContainerPresentBehavior: Boolean = false,
internal val backConfiguration: EnroBackConfiguration = EnroBackConfiguration.Default,
/**
* This Boolean sets whether or not Composables will attempt to fallback to View based animations (Animation or Animator)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,10 @@ public abstract class NavigationContainer(
public fun accept(
instruction: AnyOpenInstruction
): Boolean {
return (instructionFilter.accept(instruction) || instruction.navigationDirection == NavigationDirection.Present)
val isPresentedWithLegacyBehavior = context.controller.config.useLegacyContainerPresentBehavior
&& instruction.navigationDirection == NavigationDirection.Present

return (instructionFilter.accept(instruction) || isPresentedWithLegacyBehavior)
&& acceptedByContext(instruction)
&& canInstructionBeHostedAs(
hostType = contextType,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package dev.enro.core.container

import dev.enro.core.NavigationDirection
import dev.enro.core.NavigationInstruction
import dev.enro.core.NavigationKey
import kotlin.reflect.KClass

/**
* A NavigationContainerFilter is used to determine whether or not a given [NavigationInstruction.Open]
Expand All @@ -18,25 +18,51 @@ public class NavigationInstructionFilter internal constructor(
public class NavigationContainerFilterBuilder internal constructor() {
private val filters: MutableList<NavigationInstructionFilter> = mutableListOf()

/**
* Matches any instructions that have a NavigationKey that returns true for the provided predicate
*/
public fun key(predicate: (NavigationKey) -> Boolean) {
filters.add(NavigationInstructionFilter { predicate(it.navigationKey) })
}

/**
* Matches any instructions that have a NavigationKey that is equal to the provided key
*/
public fun key(key: NavigationKey) {
key { it == key }
}

/**
* Matches any instructions that match the provided predicate
*/
@JvmName("keyWithType")
public inline fun <reified T: NavigationKey> key(
type: KClass<T> = T::class, // can be ignored, required to disambiguate between the two key functions for the JVM
crossinline predicate: (T) -> Boolean = { true }
) {
key { it is T && predicate(it) }
}

/**
* Matches any instructions that match the provided predicate
*/
public fun instruction(predicate: (NavigationInstruction.Open<*>) -> Boolean) {
filters.add(NavigationInstructionFilter(predicate))
}

/**
* Matches any instructions that are presented (i.e. navigationDirection is NavigationDirection.Present)
*/
public fun anyPresented() {
instruction { it.navigationDirection == NavigationDirection.Present }
}

/**
* Matches any instructions that are pushed (i.e. navigationDirection is NavigationDirection.Pushed)
*/
public fun anyPushed() {
instruction { it.navigationDirection == NavigationDirection.Push }
}

internal fun build(): NavigationInstructionFilter {
return NavigationInstructionFilter { instruction ->
filters.any { it.accept(instruction) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class NavigationController internal constructor() {
private val executorRepository: ExecutorRepository = dependencyScope.get()
private val addModuleToController: AddModuleToController = dependencyScope.get()

public var config: EnroConfig = EnroConfig()
internal var config: EnroConfig = EnroConfig()
private set

init {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ package dev.enro.core.controller

import android.app.Application
import androidx.annotation.Keep
import dev.enro.core.EnroConfig

/**
* Create a NavigationController from the NavigationControllerDefinition/DSL, and immediately attach it
* to the NavigationApplication from which this function was called.
*
* @param useLegacyContainerPresentBehavior see [EnroConfig.useLegacyContainerPresentBehavior]
*/
public fun NavigationApplication.createNavigationController(
strictMode: Boolean = false,
useLegacyContainerPresentBehavior: Boolean = false,
backConfiguration: EnroBackConfiguration = EnroBackConfiguration.Default,
block: NavigationModuleScope.() -> Unit = {}
): NavigationController {
Expand All @@ -22,6 +26,7 @@ public fun NavigationApplication.createNavigationController(
setConfig(
config.copy(
isStrictMode = strictMode,
useLegacyContainerPresentBehavior = useLegacyContainerPresentBehavior,
backConfiguration = backConfiguration,
)
)
Expand All @@ -35,14 +40,21 @@ public fun NavigationApplication.createNavigationController(
)
public fun NavigationApplication.navigationController(
strictMode: Boolean = false,
useLegacyContainerPresentBehavior: Boolean = false,
backConfiguration: EnroBackConfiguration = EnroBackConfiguration.Default,
block: NavigationModuleScope.() -> Unit = {}
): NavigationController = createNavigationController(strictMode, backConfiguration, block)
): NavigationController = createNavigationController(
strictMode = strictMode,
useLegacyContainerPresentBehavior = useLegacyContainerPresentBehavior,
backConfiguration = backConfiguration,
block = block
)


@Keep // Used by EnroTest
internal fun createUnattachedNavigationController(
strictMode: Boolean = false,
useLegacyContainerPresentBehavior: Boolean = false,
backConfiguration: EnroBackConfiguration = EnroBackConfiguration.Default,
block: NavigationModuleScope.() -> Unit = {}
): NavigationController {
Expand All @@ -52,6 +64,7 @@ internal fun createUnattachedNavigationController(
setConfig(
config.copy(
isStrictMode = strictMode,
useLegacyContainerPresentBehavior = useLegacyContainerPresentBehavior,
backConfiguration = backConfiguration,
)
)
Expand Down

0 comments on commit 0ff64ba

Please sign in to comment.