diff --git a/enro-core/src/main/java/dev/enro/destination/fragment/SharedElementFragment.kt b/enro-core/src/main/java/dev/enro/destination/fragment/SharedElementFragment.kt new file mode 100644 index 00000000..cfc0ab68 --- /dev/null +++ b/enro-core/src/main/java/dev/enro/destination/fragment/SharedElementFragment.kt @@ -0,0 +1,36 @@ +package dev.enro.destination.fragment + +import android.view.View +import androidx.fragment.app.Fragment +import androidx.fragment.app.findFragment +import dev.enro.core.R +import java.util.WeakHashMap + +internal class SharedElementContainer( + val map: WeakHashMap = WeakHashMap() +) + +internal val Fragment.sharedElementContainer: SharedElementContainer? + get() = view?.getTag(R.id.enro_internal_shared_element_container_id) as? SharedElementContainer + +public fun View.addSharedElement(name: String) { + val rootFragmentView = runCatching { findFragment() } + .getOrNull() + ?.view ?: return + + val sharedElementContainer = rootFragmentView.getTag(R.id.enro_internal_shared_element_container_id) as? SharedElementContainer + ?: SharedElementContainer().apply { rootFragmentView.setTag(R.id.enro_internal_shared_element_container_id, this) } + + sharedElementContainer.map[this] = name +} + +public fun View.clearSharedElement() { + val rootFragmentView = runCatching { findFragment() } + .getOrNull() + ?.view ?: return + + val sharedElementContainer = rootFragmentView.getTag(R.id.enro_internal_shared_element_container_id) as? SharedElementContainer + ?: SharedElementContainer().apply { rootFragmentView.setTag(R.id.enro_internal_shared_element_container_id, this) } + + sharedElementContainer.map.remove(this) +} diff --git a/enro-core/src/main/java/dev/enro/destination/fragment/container/FragmentNavigationContainer.kt b/enro-core/src/main/java/dev/enro/destination/fragment/container/FragmentNavigationContainer.kt index da4648eb..6827aa58 100644 --- a/enro-core/src/main/java/dev/enro/destination/fragment/container/FragmentNavigationContainer.kt +++ b/enro-core/src/main/java/dev/enro/destination/fragment/container/FragmentNavigationContainer.kt @@ -34,6 +34,7 @@ import dev.enro.core.controller.get import dev.enro.core.controller.interceptor.builder.NavigationInterceptorBuilder import dev.enro.core.controller.usecase.HostInstructionAs import dev.enro.core.navigationContext +import dev.enro.destination.fragment.sharedElementContainer import dev.enro.extensions.animate import dev.enro.extensions.getParcelableCompat @@ -172,6 +173,9 @@ public class FragmentNavigationContainer internal constructor( ) toRemoveDirect.forEach { remove(it) + it.sharedElementContainer?.map?.forEach { + addSharedElement(it.key, it.value) + } ownedFragments.remove(it.tag) } runOnCommit { @@ -181,6 +185,9 @@ public class FragmentNavigationContainer internal constructor( } } toDetach.forEach { + it.fragment.sharedElementContainer?.map?.forEach { + addSharedElement(it.key, it.value) + } detach(it.fragment) } if (activePushed != null) { diff --git a/enro-core/src/main/res/values/id.xml b/enro-core/src/main/res/values/id.xml index 6c55075c..5ee6a69a 100644 --- a/enro-core/src/main/res/values/id.xml +++ b/enro-core/src/main/res/values/id.xml @@ -3,4 +3,5 @@ + \ No newline at end of file diff --git a/tests/application/src/main/AndroidManifest.xml b/tests/application/src/main/AndroidManifest.xml index f3a59cd8..383a1c72 100644 --- a/tests/application/src/main/AndroidManifest.xml +++ b/tests/application/src/main/AndroidManifest.xml @@ -25,6 +25,7 @@ + \ No newline at end of file diff --git a/tests/application/src/main/java/dev/enro/tests/application/fragment/FragmentSharedElement.kt b/tests/application/src/main/java/dev/enro/tests/application/fragment/FragmentSharedElement.kt new file mode 100644 index 00000000..f7b69e88 --- /dev/null +++ b/tests/application/src/main/java/dev/enro/tests/application/fragment/FragmentSharedElement.kt @@ -0,0 +1,112 @@ +package dev.enro.tests.application.fragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.ImageView +import androidx.appcompat.app.AppCompatActivity +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.material.Button +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import androidx.fragment.app.Fragment +import androidx.transition.TransitionInflater +import dev.enro.annotations.NavigationDestination +import dev.enro.core.NavigationKey +import dev.enro.core.compose.navigationHandle +import dev.enro.core.fragment.container.navigationContainer +import dev.enro.core.navigationHandle +import dev.enro.core.push +import dev.enro.core.requestClose +import dev.enro.destination.fragment.addSharedElement +import dev.enro.test.application.R +import kotlinx.parcelize.Parcelize + +@Parcelize +object FragmentSharedElementDestination : NavigationKey.SupportsPresent { + @Parcelize + internal class FirstFragmentDestination : NavigationKey.SupportsPush + @Parcelize + internal class SecondFragmentDestination : NavigationKey.SupportsPush +} + +@NavigationDestination(FragmentSharedElementDestination::class) +class FragmentSharedElementActivity : AppCompatActivity() { + val container by navigationContainer( + containerId = R.id.fragment_container, + root = { FragmentSharedElementDestination.FirstFragmentDestination() } + ) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.fragment_shared_element_activity) + } +} + +@NavigationDestination(FragmentSharedElementDestination.FirstFragmentDestination::class) +class FragmentSharedElementFirstFragment : Fragment() { + + private val navigation by navigationHandle() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + sharedElementEnterTransition = TransitionInflater.from(requireContext()) + .inflateTransition(android.R.transition.move) + sharedElementReturnTransition = TransitionInflater.from(requireContext()) + .inflateTransition(android.R.transition.move) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_shared_element_first, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + view.findViewById(R.id.shared_image) + .addSharedElement("fragment_image") + + view.findViewById