Skip to content

Commit

Permalink
Initial work to provide better transition animations between destinat…
Browse files Browse the repository at this point in the history
…ions
  • Loading branch information
jayohms committed Jan 5, 2024
1 parent b5ba032 commit a071acb
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 82 deletions.
26 changes: 0 additions & 26 deletions demo/src/main/kotlin/dev/hotwire/turbo/demo/base/NavDestination.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,10 @@ import android.view.MenuItem
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.browser.customtabs.CustomTabsIntent
import androidx.browser.customtabs.CustomTabsIntent.SHARE_STATE_ON
import androidx.navigation.NavOptions
import androidx.navigation.navOptions
import dev.hotwire.strada.BridgeDestination
import dev.hotwire.turbo.config.TurboPathConfigurationProperties
import dev.hotwire.turbo.config.context
import dev.hotwire.turbo.demo.R
import dev.hotwire.turbo.demo.util.BASE_URL
import dev.hotwire.turbo.nav.TurboNavDestination
import dev.hotwire.turbo.nav.TurboNavPresentationContext.MODAL

interface NavDestination : TurboNavDestination, BridgeDestination {
val menuProgress: MenuItem?
Expand All @@ -29,16 +24,6 @@ interface NavDestination : TurboNavDestination, BridgeDestination {
}
}

override fun getNavigationOptions(
newLocation: String,
newPathProperties: TurboPathConfigurationProperties
): NavOptions {
return when (newPathProperties.context) {
MODAL -> slideAnimation()
else -> super.getNavigationOptions(newLocation, newPathProperties)
}
}

override fun bridgeWebViewIsReady(): Boolean {
return session.isReady
}
Expand All @@ -63,15 +48,4 @@ interface NavDestination : TurboNavDestination, BridgeDestination {
.build()
.launchUrl(context, Uri.parse(location))
}

private fun slideAnimation(): NavOptions {
return navOptions {
anim {
enter = R.anim.nav_slide_enter
exit = R.anim.nav_slide_exit
popEnter = R.anim.nav_slide_pop_enter
popExit = R.anim.nav_slide_pop_exit
}
}
}
}
10 changes: 5 additions & 5 deletions turbo/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'org.jetbrains.kotlin:kotlin-reflect:1.9.10'
implementation 'com.google.android.material:material:1.10.0'
implementation 'com.google.android.material:material:1.11.0'

// AndroidX
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
Expand All @@ -95,14 +95,14 @@ dependencies {
api 'androidx.appcompat:appcompat:1.6.1'
api 'androidx.core:core-ktx:1.12.0'
api 'androidx.webkit:webkit:1.8.0'
api 'androidx.activity:activity-ktx:1.8.1'
api 'androidx.activity:activity-ktx:1.8.2'
api 'androidx.fragment:fragment-ktx:1.6.2'
api 'androidx.navigation:navigation-fragment-ktx:2.7.5'
api 'androidx.navigation:navigation-ui-ktx:2.7.5'
api 'androidx.navigation:navigation-fragment-ktx:2.7.6'
api 'androidx.navigation:navigation-ui-ktx:2.7.6'

// Tests
testImplementation 'androidx.test:core:1.5.0' // Robolectric
testImplementation 'androidx.navigation:navigation-testing:2.7.5'
testImplementation 'androidx.navigation:navigation-testing:2.7.6'
testImplementation 'androidx.arch.core:core-testing:2.2.0'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
testImplementation 'org.assertj:assertj-core:3.24.2'
Expand Down
8 changes: 8 additions & 0 deletions turbo/src/main/assets/json/test-configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@
"presentation": "none"
}
},
{
"patterns": [
"/custom/replace-root"
],
"properties": {
"presentation": "replace_root"
}
},
{
"patterns": [
"/custom/modal"
Expand Down
22 changes: 0 additions & 22 deletions turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavDestination.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@ import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.navigation.NavController
import androidx.navigation.NavOptions
import androidx.navigation.fragment.FragmentNavigator
import androidx.navigation.fragment.findNavController
import androidx.navigation.navOptions
import androidx.navigation.ui.R
import dev.hotwire.turbo.config.TurboPathConfiguration
import dev.hotwire.turbo.config.TurboPathConfigurationProperties
import dev.hotwire.turbo.delegates.TurboFragmentDelegate
Expand Down Expand Up @@ -157,25 +154,6 @@ interface TurboNavDestination {
navigator.navigate(location, options, bundle, extras)
}

/**
* Gets the default set of navigation options (basic enter/exit animations) for the Android
* Navigation component to use to execute a navigation event. This can be overridden if
* you'd like to provide your own.
*/
fun getNavigationOptions(
newLocation: String,
newPathProperties: TurboPathConfigurationProperties
): NavOptions {
return navOptions {
anim {
enter = R.anim.nav_default_enter_anim
exit = R.anim.nav_default_exit_anim
popEnter = R.anim.nav_default_pop_enter_anim
popExit = R.anim.nav_default_pop_exit_anim
}
}
}

/**
* Navigates up to the previous destination. See [NavController.navigateUp] for
* more details.
Expand Down
13 changes: 5 additions & 8 deletions turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavRule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ internal class TurboNavRule(
location: String,
visitOptions: TurboVisitOptions,
bundle: Bundle?,
navOptions: NavOptions,
extras: FragmentNavigator.Extras?,
pathConfiguration: TurboPathConfiguration,
val controller: NavController
Expand All @@ -46,7 +45,7 @@ internal class TurboNavRule(
val newFallbackUri = newProperties.fallbackUri
val newDestination = controller.destinationFor(newDestinationUri)
val newFallbackDestination = controller.destinationFor(newFallbackUri)
val newNavOptions = newNavOptions(navOptions)
val newNavOptions = newNavOptions()

init {
verifyNavRules()
Expand Down Expand Up @@ -76,15 +75,13 @@ internal class TurboNavRule(
}
}

private fun newNavOptions(navOptions: NavOptions): NavOptions {
// Use separate NavOptions if we need to pop up to the new root destination
if (newPresentation == TurboNavPresentation.REPLACE_ROOT && newDestination != null) {
return navOptions {
private fun newNavOptions(): NavOptions {
return navOptions {
if (newPresentation == TurboNavPresentation.REPLACE_ROOT && newDestination != null) {
// Pop up to the new root destination
popUpTo(newDestination.id) { inclusive = true }
}
}

return navOptions
}

private fun newNavigationMode(): TurboNavMode {
Expand Down
11 changes: 0 additions & 11 deletions turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavigator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import android.os.Bundle
import androidx.fragment.app.DialogFragment
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavController
import androidx.navigation.NavOptions
import androidx.navigation.fragment.FragmentNavigator
import androidx.navigation.fragment.findNavController
import dev.hotwire.turbo.util.location
Expand Down Expand Up @@ -47,7 +46,6 @@ internal class TurboNavigator(private val navDestination: TurboNavDestination) {
location = location,
visitOptions = options,
bundle = bundle,
navOptions = navOptions(location),
extras = extras,
pathConfiguration = session.pathConfiguration,
controller = currentControllerForLocation(location)
Expand Down Expand Up @@ -270,15 +268,6 @@ internal class TurboNavigator(private val navDestination: TurboNavDestination) {
return shouldNavigate
}

private fun navOptions(location: String): NavOptions {
val properties = session.pathConfiguration.properties(location)

return navDestination.getNavigationOptions(
newLocation = location,
newPathProperties = properties
)
}

private val NavBackStackEntry?.isModalContext: Boolean
get() {
val context = this?.arguments?.getSerializable("presentation-context")
Expand Down
37 changes: 27 additions & 10 deletions turbo/src/test/kotlin/dev/hotwire/turbo/nav/TurboNavRuleTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import androidx.navigation.createGraph
import androidx.navigation.navOptions
import androidx.navigation.testing.TestNavHostController
import androidx.test.core.app.ApplicationProvider
import androidx.navigation.ui.R
import dev.hotwire.turbo.config.TurboPathConfiguration
import dev.hotwire.turbo.visit.TurboVisitOptions
import org.assertj.core.api.Assertions.assertThat
Expand All @@ -37,6 +36,7 @@ class TurboNavRuleTest {
private val recedeUrl = "https://hotwired.dev/custom/recede"
private val refreshUrl = "https://hotwired.dev/custom/refresh"
private val resumeUrl = "https://hotwired.dev/custom/resume"
private val replaceRootUrl = "https://hotwired.dev/custom/replace-root"
private val modalRootUrl = "https://hotwired.dev/custom/modal"
private val filterUrl = "https://hotwired.dev/feature?filter=true"
private val customUrl = "https://hotwired.dev/custom"
Expand All @@ -51,14 +51,7 @@ class TurboNavRuleTest {
private val webHomeUri = Uri.parse("turbo://fragment/web/home")

private val extras = null
private val navOptions = navOptions {
anim {
enter = R.anim.nav_default_enter_anim
exit = R.anim.nav_default_exit_anim
popEnter = R.anim.nav_default_pop_enter_anim
popExit = R.anim.nav_default_pop_exit_anim
}
}
private val navOptions = navOptions {}

@Before
fun setup() {
Expand Down Expand Up @@ -113,6 +106,30 @@ class TurboNavRuleTest {
assertThat(rule.newNavOptions).isEqualTo(navOptions)
}

@Test
fun `navigate replacing the root`() {
val rule = getNavigatorRule(replaceRootUrl)

// Current destination
assertThat(rule.previousLocation).isNull()
assertThat(rule.currentLocation).isEqualTo(homeUrl)
assertThat(rule.currentPresentationContext).isEqualTo(TurboNavPresentationContext.DEFAULT)
assertThat(rule.isAtStartDestination).isTrue()

// New destination
assertThat(rule.newLocation).isEqualTo(replaceRootUrl)
assertThat(rule.newPresentationContext).isEqualTo(TurboNavPresentationContext.DEFAULT)
assertThat(rule.newPresentation).isEqualTo(TurboNavPresentation.REPLACE_ROOT)
assertThat(rule.newQueryStringPresentation).isEqualTo(TurboNavQueryStringPresentation.DEFAULT)
assertThat(rule.newNavigationMode).isEqualTo(TurboNavMode.IN_CONTEXT)
assertThat(rule.newModalResult).isNull()
assertThat(rule.newDestinationUri).isEqualTo(webUri)
assertThat(rule.newDestination).isNotNull()
assertThat(rule.newNavOptions).isEqualTo(navOptions {
popUpTo(webDestinationId) { inclusive = true }
})
}

@Test
fun `navigate to modal context replacing root`() {
assertThatThrownBy { getNavigatorRule(modalRootUrl) }
Expand Down Expand Up @@ -359,7 +376,7 @@ class TurboNavRuleTest {
bundle: Bundle? = null
): TurboNavRule {
return TurboNavRule(
location, visitOptions, bundle, navOptions, extras, pathConfiguration, controller
location, visitOptions, bundle, extras, pathConfiguration, controller
)
}

Expand Down

0 comments on commit a071acb

Please sign in to comment.