diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt index e02e98c3a8..2bb8e0d6ae 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt @@ -3,8 +3,6 @@ package com.swmansion.rnscreens import android.content.Context import android.graphics.Canvas import android.view.View -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction import com.facebook.react.bridge.ReactContext import com.facebook.react.uimanager.UIManagerModule @@ -20,21 +18,6 @@ class ScreenStack(context: Context?) : ScreenContainer(cont private val drawingOpPool: MutableList = ArrayList() private val drawingOps: MutableList = ArrayList() private var mTopScreen: ScreenStackFragment? = null - private val mBackStackListener = FragmentManager.OnBackStackChangedListener { - if (mFragmentManager?.backStackEntryCount == 0) { - // when back stack entry count hits 0 it means the user's navigated back using hw back - // button. As the "fake" transaction we installed on the back stack does nothing we need - // to handle back navigation on our own. - mTopScreen?.let { dismiss(it) } - } - } - private val mLifecycleCallbacks: FragmentManager.FragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { - override fun onFragmentResumed(fm: FragmentManager, f: Fragment) { - if (mTopScreen === f) { - setupBackHandlerIfNeeded(f) - } - } - } private var mRemovalTransitionStarted = false private var isDetachingCurrentScreen = false private var reverseLastTwoChildren = false @@ -65,28 +48,6 @@ class ScreenStack(context: Context?) : ScreenContainer(cont return ScreenStackFragment(screen) } - override fun onDetachedFromWindow() { - mFragmentManager?.let { - it.removeOnBackStackChangedListener(mBackStackListener) - it.unregisterFragmentLifecycleCallbacks(mLifecycleCallbacks) - if (!it.isStateSaved && !it.isDestroyed) { - // State save means that the container where fragment manager was installed has been - // unmounted. - // This could happen as a result of dismissing nested stack. In such a case we don't need to - // reset back stack as it'd result in a crash caused by the fact the fragment manager is no - // longer attached. - it.popBackStack(BACK_STACK_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE) - } - } - super.onDetachedFromWindow() - } - - override fun onAttachedToWindow() { - super.onAttachedToWindow() - val fragmentManager = requireNotNull(mFragmentManager, { "mFragmentManager is null when ScreenStack attached to window" }) - fragmentManager.registerFragmentLifecycleCallbacks(mLifecycleCallbacks, false) - } - override fun startViewTransition(view: View) { super.startViewTransition(view) mRemovalTransitionStarted = true @@ -278,7 +239,6 @@ class ScreenStack(context: Context?) : ScreenContainer(cont mStack.clear() mStack.addAll(mScreenFragments) it.commitNowAllowingStateLoss() - mTopScreen?.let { screen -> setupBackHandlerIfNeeded(screen) } } } @@ -288,61 +248,6 @@ class ScreenStack(context: Context?) : ScreenContainer(cont } } - /** - * The below method sets up fragment manager's back stack in a way that it'd trigger our back - * stack change listener when hw back button is clicked. - * - * - * Because back stack by default rolls back the transaction the stack entry is associated with - * we generate a "fake" transaction that hides and shows the top fragment. As a result when back - * stack entry is rolled back nothing happens and we are free to handle back navigation on our own - * in `mBackStackListener`. - * - * - * We pop that "fake" transaction each time we update stack and we add a new one in case the - * top screen is allowed to be dismissed using hw back button. This way in the listener we can - * tell if back button was pressed based on the count of the items on back stack. We expect 0 - * items in case hw back is pressed because we try to keep the number of items at 1 by always - * resetting and adding new items. In case we don't add a new item to back stack we remove - * listener so that it does not get triggered. - * - * - * It is important that we don't install back handler when stack contains a single screen as in - * that case we want the parent navigator or activity handler to take over. - */ - private fun setupBackHandlerIfNeeded(topScreen: ScreenStackFragment) { - if (mTopScreen?.isResumed != true) { - // if the top fragment is not in a resumed state, adding back stack transaction would throw. - // In such a case we skip installing back handler and use FragmentLifecycleCallbacks to get - // notified when it gets resumed so that we can install the handler. - return - } - mFragmentManager?.let { - it.removeOnBackStackChangedListener(mBackStackListener) - it.popBackStack(BACK_STACK_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE) - var firstScreen: ScreenStackFragment? = null - var i = 0 - val size = mStack.size - while (i < size) { - val screen = mStack[i] - if (!mDismissed.contains(screen)) { - firstScreen = screen - break - } - i++ - } - if (topScreen !== firstScreen && topScreen.isDismissible) { - it - .beginTransaction() - .show(topScreen) - .addToBackStack(BACK_STACK_TAG) - .setPrimaryNavigationFragment(topScreen) - .commitNowAllowingStateLoss() - it.addOnBackStackChangedListener(mBackStackListener) - } - } - } - // below methods are taken from // https://github.com/airbnb/native-navigation/blob/9cf50bf9b751b40778f473f3b19fcfe2c4d40599/lib/android/src/main/java/com/airbnb/android/react/navigation/ScreenCoordinatorLayout.java#L43 // and are used to swap the order of drawing views when navigating forward with the transitions @@ -418,7 +323,6 @@ class ScreenStack(context: Context?) : ScreenContainer(cont } companion object { - private const val BACK_STACK_TAG = "RN_SCREEN_LAST" private fun isSystemAnimation(stackAnimation: StackAnimation): Boolean { return stackAnimation === StackAnimation.DEFAULT || stackAnimation === StackAnimation.FADE || stackAnimation === StackAnimation.NONE } diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt index 5625b7411a..ea6d8846a1 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt @@ -145,9 +145,6 @@ class ScreenStackFragment : ScreenFragment { return view } - val isDismissible: Boolean - get() = screen.isGestureEnabled - fun canNavigateBack(): Boolean { val container: ScreenContainer<*>? = screen.container check(container is ScreenStack) { "ScreenStackFragment added into a non-stack container" } diff --git a/guides/GUIDE_FOR_LIBRARY_AUTHORS.md b/guides/GUIDE_FOR_LIBRARY_AUTHORS.md index 5cb4c962ab..a7c3162d6d 100644 --- a/guides/GUIDE_FOR_LIBRARY_AUTHORS.md +++ b/guides/GUIDE_FOR_LIBRARY_AUTHORS.md @@ -412,3 +412,7 @@ Check [LifecycleAwareView.java](https://github.com/kmagiera/react-native-screens In addition to that, you will need to register for receiving these updates. This can be done using [`LifecycleHelper.register`](https://github.com/kmagiera/react-native-screens/blob/master/android/src/main/java/com/swmansion/rnscreens/LifecycleHelper.java#L50). Remember to call [`LifecycleHelper.unregister`](https://github.com/kmagiera/react-native-screens/blob/master/android/src/main/java/com/swmansion/rnscreens/LifecycleHelper.java#L59) before the view is dropped. Please refer to [SampleLifecycleAwareViewManager.java](https://github.com/kmagiera/react-native-screens/blob/master/Example/android/app/src/main/java/com/swmansion/rnscreens/example/SampleLifecycleAwareViewManager.java) from our example app to see what are the best ways of using the above methods. + +## Android hardware back button + +In order to properly handle the hardware back button on Android, you should implement the navigation logic concerning it. You can see an example of how it is done in `react-navigation` here: https://github.com/react-navigation/react-navigation/blob/6cba517b74f5fd092db21d5574b558ef2d80897b/packages/native/src/useBackButton.tsx.