Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class HotwireActivityDelegate(val activity: HotwireActivity) {
internal fun registerNavigatorHost(host: NavigatorHost) {
logDebug("navigatorRegistered", listOf("navigator" to host.navigator.configuration.name))

if (navigatorHosts[host.id] == null) {
if (navigatorHosts[host.id] !== host) {
navigatorHosts[host.id] = host
listenToDestinationChanges(host)

Expand All @@ -105,7 +105,11 @@ class HotwireActivityDelegate(val activity: HotwireActivity) {

internal fun unregisterNavigatorHost(host: NavigatorHost) {
logDebug("navigatorUnregistered", listOf("navigator" to host.navigator.configuration.name))
navigatorHosts.remove(host.id)

// A newer host instance can take over the same ID before this one is destroyed.
if (navigatorHosts[host.id] === host) {
navigatorHosts.remove(host.id)
}
}

internal fun onNavigatorHostReady(host: NavigatorHost) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package dev.hotwire.navigation.activities

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.fragment.app.Fragment
import dev.hotwire.navigation.navigator.NavigatorConfiguration
import dev.hotwire.navigation.navigator.NavigatorHost
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner

// A screen that hosts a nested NavigatorHost can be replaced by a new instance of itself. The new
// nested host is created before the old one is destroyed, and both use the same view ID.
@RunWith(RobolectricTestRunner::class)
class HotwireActivityDelegateTest {

private lateinit var activity: TestActivity

@Before
fun setUp() {
activity = Robolectric.buildActivity(TestActivity::class.java).setup().get()
}

@Test
fun `new nested host loads its graph while the old host with the same id is still registered`() {
addScreen(CONTAINER_A_ID)

val newScreen = addScreen(CONTAINER_B_ID)

assertThat(newScreen.navigatorHost.isGraphInitialized).isTrue()
}

@Test
fun `destroying the old nested host keeps the new host with the same id registered`() {
val oldScreen = addScreen(CONTAINER_A_ID)
val newScreen = addScreen(CONTAINER_B_ID)

activity.supportFragmentManager.beginTransaction().remove(oldScreen).commitNow()

assertThat(activity.delegate.findNavigatorHost(NESTED_HOST_ID))
.isSameAs(newScreen.navigatorHost)
}

private fun addScreen(containerId: Int): NestedHostScreen {
val screen = NestedHostScreen()
activity.supportFragmentManager.beginTransaction().add(containerId, screen).commitNow()
return screen
}

class NestedHostScreen : Fragment() {
val navigatorHost get() = childFragmentManager.findFragmentById(NESTED_HOST_ID) as NavigatorHost

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return FrameLayout(requireContext()).apply { id = NESTED_HOST_ID }
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
childFragmentManager.beginTransaction().add(NESTED_HOST_ID, NavigatorHost()).commitNow()
}
}

class TestActivity : HotwireActivity() {
override fun navigatorConfigurations() = listOf(
NavigatorConfiguration(
name = "nested",
startLocation = "https://example.com/start",
navigatorHostId = NESTED_HOST_ID
)
)

override fun onCreate(savedInstanceState: Bundle?) {
setTheme(androidx.appcompat.R.style.Theme_AppCompat_Light_NoActionBar)
super.onCreate(savedInstanceState)
setContentView(LinearLayout(this).apply {
addView(FrameLayout(context).apply { id = CONTAINER_A_ID })
addView(FrameLayout(context).apply { id = CONTAINER_B_ID })
})
}
}

companion object {
private val NESTED_HOST_ID = View.generateViewId()
private val CONTAINER_A_ID = View.generateViewId()
private val CONTAINER_B_ID = View.generateViewId()
}
}
Loading