From d5e81b35005e9f530fe69c772ea6c0f3b4f46019 Mon Sep 17 00:00:00 2001 From: erolaksoy Date: Tue, 17 Dec 2024 17:29:23 +0300 Subject: [PATCH 1/8] Add preload support to Navigator with preloadFragment and startPreloadFragment methods --- .../navigator/MultipleStackNavigator.kt | 14 +++++++++ .../trendyol/medusalib/navigator/Navigator.kt | 31 +++++++++++++++++++ .../controller/FragmentManagerController.kt | 26 ++++++++++++++++ .../controller/PreloadedFragmentResult.kt | 10 ++++++ 4 files changed, 81 insertions(+) create mode 100644 medusalib/src/main/java/com/trendyol/medusalib/navigator/controller/PreloadedFragmentResult.kt diff --git a/medusalib/src/main/java/com/trendyol/medusalib/navigator/MultipleStackNavigator.kt b/medusalib/src/main/java/com/trendyol/medusalib/navigator/MultipleStackNavigator.kt index a6a7842..c1e1b7b 100755 --- a/medusalib/src/main/java/com/trendyol/medusalib/navigator/MultipleStackNavigator.kt +++ b/medusalib/src/main/java/com/trendyol/medusalib/navigator/MultipleStackNavigator.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import com.trendyol.medusalib.navigator.controller.FragmentManagerController +import com.trendyol.medusalib.navigator.controller.PreloadedFragmentResult import com.trendyol.medusalib.navigator.controller.StagedFragmentHolder import com.trendyol.medusalib.navigator.data.FragmentData import com.trendyol.medusalib.navigator.data.StackItem @@ -58,6 +59,19 @@ open class MultipleStackNavigator( start(fragment, DEFAULT_GROUP_NAME, transitionAnimation) } + override fun preloadFragment(fragment: Fragment, fragmentTag: String) { + fragmentManagerController.preloadFragment(FragmentData(fragment, fragmentTag)) + } + + override fun startPreloadFragment(fallbackFragment: Fragment?, fragmentTag: String): PreloadedFragmentResult { + val currentFragmentTag = getCurrentFragmentTag() + val result = fragmentManagerController.showPreloadedFragment(currentFragmentTag, fragmentTag, fallbackFragment) + if (result !is PreloadedFragmentResult.NotFound) { + fragmentStackState.notifyStackItemAddToCurrentTab(StackItem(fragmentTag = fragmentTag)) + } + return result + } + override fun start(fragment: Fragment, fragmentGroupName: String, transitionAnimation: TransitionAnimationType?) { val createdTag = tagCreator.create(fragment) diff --git a/medusalib/src/main/java/com/trendyol/medusalib/navigator/Navigator.kt b/medusalib/src/main/java/com/trendyol/medusalib/navigator/Navigator.kt index ff5515e..54d1dd8 100755 --- a/medusalib/src/main/java/com/trendyol/medusalib/navigator/Navigator.kt +++ b/medusalib/src/main/java/com/trendyol/medusalib/navigator/Navigator.kt @@ -3,6 +3,7 @@ package com.trendyol.medusalib.navigator import android.os.Bundle import androidx.fragment.app.Fragment import androidx.lifecycle.LifecycleOwner +import com.trendyol.medusalib.navigator.controller.PreloadedFragmentResult import com.trendyol.medusalib.navigator.transaction.NavigatorTransaction import com.trendyol.medusalib.navigator.transitionanimation.TransitionAnimationType @@ -69,6 +70,36 @@ interface Navigator { */ fun start(fragment: Fragment, transitionAnimation: TransitionAnimationType) + /** + * Preloads a fragment into the current navigation stack without immediately displaying it. + * + * This method attaches the given fragment to the fragment manager and prepares it in a hidden. + * The fragment will not be visible to the user until it is started later using + * [startPreloadFragment]. + * + * @param fragment The fragment instance to preload. + * @param fragmentTag The unique tag of fragment. + */ + fun preloadFragment(fragment: Fragment, fragmentTag: String) + + /** + * Starts a fragment that was previously preloaded, making it visible. + * + * This method takes a previously preloaded fragment identified by its [fragmentTag] and brings it + * to the foreground. If the fragment was successfully preloaded, it will be shown immediately. If + * the fragment was not found or not preloaded, the optionally provided [fallbackFragment] will be added + * and displayed as a fallback. + * + * @param fallbackFragment Fragment instance to display if the preloaded fragment cannot be found. + * @param fragmentTag The unique tag of the previously preloaded fragment to start. + * + * @return [PreloadedFragmentResult] + * - [PreloadedFragmentResult.Success] if the preloaded fragment was found and displayed. + * - [PreloadedFragmentResult.FallbackSuccess] if the fallback fragment was used instead. + * - [PreloadedFragmentResult.NotFound] if no suitable fragment was found and no fallback was provided. + */ + fun startPreloadFragment(fallbackFragment: Fragment?, fragmentTag: String): PreloadedFragmentResult + /** * Modifies fragment stack. Pops current fragment from * fragment stack and detaches it. Peeks from fragment stack diff --git a/medusalib/src/main/java/com/trendyol/medusalib/navigator/controller/FragmentManagerController.kt b/medusalib/src/main/java/com/trendyol/medusalib/navigator/controller/FragmentManagerController.kt index 2f2e6b6..d53c9b0 100644 --- a/medusalib/src/main/java/com/trendyol/medusalib/navigator/controller/FragmentManagerController.kt +++ b/medusalib/src/main/java/com/trendyol/medusalib/navigator/controller/FragmentManagerController.kt @@ -78,6 +78,32 @@ internal class FragmentManagerController( commitAllowingStateLoss() } + fun preloadFragment(fragmentData: FragmentData) { + checkAndCreateTransaction() + stageFragment(fragmentData) + currentTransaction?.add(containerId, fragmentData.fragment, fragmentData.fragmentTag)?.hide(fragmentData.fragment) + commitAllowingStateLoss() + } + + fun showPreloadedFragment( + currentFragmentTag: String, + fragmentTag: String, + fallbackFragment: Fragment?, + ): PreloadedFragmentResult { + val fragment = getFragmentWithExecutingPendingTransactionsIfNeeded(fragmentTag) ?: fallbackFragment + if (fragment == null) return PreloadedFragmentResult.NotFound + + disableFragment(currentFragmentTag) + + return if (fragment != fallbackFragment) { + enableFragment(fragmentTag) + PreloadedFragmentResult.Success + } else { + addFragment(FragmentData(fallbackFragment, fragmentTag)) + PreloadedFragmentResult.FallbackSuccess + } + } + fun disableAndStartFragment(disableFragmentTag: String, vararg fragmentDataArgs: FragmentData) { val disabledFragment = getFragmentWithExecutingPendingTransactionsIfNeeded(disableFragmentTag) diff --git a/medusalib/src/main/java/com/trendyol/medusalib/navigator/controller/PreloadedFragmentResult.kt b/medusalib/src/main/java/com/trendyol/medusalib/navigator/controller/PreloadedFragmentResult.kt new file mode 100644 index 0000000..fb43523 --- /dev/null +++ b/medusalib/src/main/java/com/trendyol/medusalib/navigator/controller/PreloadedFragmentResult.kt @@ -0,0 +1,10 @@ +package com.trendyol.medusalib.navigator.controller + +sealed interface PreloadedFragmentResult { + + data object Success: PreloadedFragmentResult + + data object FallbackSuccess: PreloadedFragmentResult + + data object NotFound: PreloadedFragmentResult +} \ No newline at end of file From acb1a919bc23cb73f0df3bd7d152b1cabe6c45e1 Mon Sep 17 00:00:00 2001 From: erolaksoy Date: Tue, 17 Dec 2024 17:32:16 +0300 Subject: [PATCH 2/8] Add sample preload fragment --- .../java/com/trendyol/medusa/MainActivity.kt | 8 +++ .../trendyol/medusa/SamplePreloadFragment.kt | 49 +++++++++++++++++++ app/src/main/res/layout/activity_main.xml | 13 +++++ .../res/layout/fragment_sample_preload.xml | 14 ++++++ 4 files changed, 84 insertions(+) create mode 100644 app/src/main/java/com/trendyol/medusa/SamplePreloadFragment.kt create mode 100644 app/src/main/res/layout/fragment_sample_preload.xml diff --git a/app/src/main/java/com/trendyol/medusa/MainActivity.kt b/app/src/main/java/com/trendyol/medusa/MainActivity.kt index d38a3ab..772be3a 100644 --- a/app/src/main/java/com/trendyol/medusa/MainActivity.kt +++ b/app/src/main/java/com/trendyol/medusa/MainActivity.kt @@ -64,6 +64,11 @@ class MainActivity : AppCompatActivity(), Navigator.NavigatorListener { navigation = findViewById(R.id.navigation) as BottomNavigationView + multipleStackNavigator.preloadFragment( + fragment = SamplePreloadFragment.newInstance(), + fragmentTag = SamplePreloadFragment.TAG + ) + multipleStackNavigator.initialize(savedInstanceState) val restartRootFragmentCheckBox = findViewById(R.id.restartSwitch) as SwitchCompat findViewById