diff --git a/README.md b/README.md index d441f58..3918c77 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ getCurrentFragment()?.let {
Let the fragment define behavioural patterns of it's own by overriding open properties: ``` kotlin open val isModal: Boolean +open val mustBeValidToInvokeNavigation: Boolean open val overridesBackPress: Boolean open val animationEnter: Int open val animationExit: Int diff --git a/app/src/main/kotlin/com/vojtkovszky/singleactivitynavigationexample/MainFragment.kt b/app/src/main/kotlin/com/vojtkovszky/singleactivitynavigationexample/MainFragment.kt index 28a9add..8431f21 100644 --- a/app/src/main/kotlin/com/vojtkovszky/singleactivitynavigationexample/MainFragment.kt +++ b/app/src/main/kotlin/com/vojtkovszky/singleactivitynavigationexample/MainFragment.kt @@ -13,7 +13,10 @@ class MainFragment : BaseSingleFragment() { private var _binding: FragmentMainBinding? = null private val binding get() = _binding!! - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override val mustBeValidToInvokeNavigation: Boolean + get() = true + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = FragmentMainBinding.inflate(inflater, container, false) return binding.root } diff --git a/singleactivitynavigation/src/main/kotlin/com/vojtkovszky/singleactivitynavigation/BaseSingleFragment.kt b/singleactivitynavigation/src/main/kotlin/com/vojtkovszky/singleactivitynavigation/BaseSingleFragment.kt index 8b59427..7f75034 100644 --- a/singleactivitynavigation/src/main/kotlin/com/vojtkovszky/singleactivitynavigation/BaseSingleFragment.kt +++ b/singleactivitynavigation/src/main/kotlin/com/vojtkovszky/singleactivitynavigation/BaseSingleFragment.kt @@ -34,6 +34,15 @@ abstract class BaseSingleFragment: Fragment() { open val isModal: Boolean get() = fragmentType == FragmentType.MODAL + /** + * If set to true, any navigation call from this fragment will check for fragment validity + * first; meaning fragment has to be added, its activity not null, not destroyed, not finishing. + * This ensures navigation methods are never invoked outside of fragment lifecycle. + * false is default. + */ + open val mustBeValidToInvokeNavigation: Boolean + get() = false + /** * If set to true, [BaseSingleActivity]'s onBackPressed method will be prevented from invoking. */ @@ -82,28 +91,36 @@ abstract class BaseSingleFragment: Fragment() { * Shortcut to [BaseSingleActivity.navigateBackTo] */ fun navigateBackTo(fragmentName: String) { - baseSingleActivity?.navigateBackTo(fragmentName) + if (canProceedWithNavigation()) { + baseSingleActivity?.navigateBackTo(fragmentName) + } } /** * Shortcut to [BaseSingleActivity.navigateBack] */ fun navigateBack() { - baseSingleActivity?.navigateBack() + if (canProceedWithNavigation()) { + baseSingleActivity?.navigateBack() + } } /** * Shortcut to [BaseSingleActivity.navigateBackToRoot] */ fun navigateBackToRoot() { - baseSingleActivity?.navigateBackToRoot() + if (canProceedWithNavigation()) { + baseSingleActivity?.navigateBackToRoot() + } } /** * Shortcut to [BaseSingleActivity.selectRootFragment] */ fun selectRootFragment(positionIndex: Int = 0, popStack: Boolean = true) { - baseSingleActivity?.selectRootFragment(positionIndex, popStack) + if (canProceedWithNavigation()) { + baseSingleActivity?.selectRootFragment(positionIndex, popStack) + } } /** @@ -111,14 +128,18 @@ abstract class BaseSingleFragment: Fragment() { */ fun navigateTo(fragment: BaseSingleFragment, openAsModal: Boolean = false, ignoreIfAlreadyInStack: Boolean = false) { - baseSingleActivity?.navigateTo(fragment, openAsModal, ignoreIfAlreadyInStack) + if (canProceedWithNavigation()) { + baseSingleActivity?.navigateTo(fragment, openAsModal, ignoreIfAlreadyInStack) + } } /** * Shortcut to [BaseSingleActivity.openBottomSheet] */ fun openBottomSheet(fragment: BaseSingleFragment) { - baseSingleActivity?.openBottomSheet(fragment) + if (canProceedWithNavigation()) { + baseSingleActivity?.openBottomSheet(fragment) + } } /** @@ -126,21 +147,27 @@ abstract class BaseSingleFragment: Fragment() { */ fun openDialog(fragment: BaseSingleFragment, anchorView: View? = null, useFullWidth: Boolean = true, dialogStyle: Int = DialogFragment.STYLE_NORMAL, dialogTheme: Int = 0) { - baseSingleActivity?.openDialog(fragment, anchorView, useFullWidth, dialogStyle, dialogTheme) + if (canProceedWithNavigation()) { + baseSingleActivity?.openDialog(fragment, anchorView, useFullWidth, dialogStyle, dialogTheme) + } } /** * Shortcut to [BaseSingleActivity.closeCurrentlyOpenBottomSheet] */ fun closeCurrentlyOpenBottomSheet() { - baseSingleActivity?.closeCurrentlyOpenBottomSheet() + if (canProceedWithNavigation()) { + baseSingleActivity?.closeCurrentlyOpenBottomSheet() + } } /** * Shortcut to [BaseSingleActivity.closeCurrentlyOpenDialog] */ fun closeCurrentlyOpenDialog() { - baseSingleActivity?.closeCurrentlyOpenDialog() + if (canProceedWithNavigation()) { + baseSingleActivity?.closeCurrentlyOpenDialog() + } } // endregion shortcuts to baseSingleActivity methods @@ -165,6 +192,17 @@ abstract class BaseSingleFragment: Fragment() { } // endregion add bundle + // region private methods + private fun canProceedWithNavigation(): Boolean { + return if (mustBeValidToInvokeNavigation) isValid() else true + } + + private fun isValid(): Boolean { + return baseSingleActivity != null && !baseSingleActivity!!.isDestroyed && + !baseSingleActivity!!.isFinishing && isAdded + } + // endregion private methods + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { ViewCompat.setTranslationZ(view, translationZ) super.onViewCreated(view, savedInstanceState)