diff --git a/android/src/main/java/com/reactnativenavigation/hierarchy/root/RootAnimator.kt b/android/src/main/java/com/reactnativenavigation/hierarchy/root/RootAnimator.kt index fd721465fa9..41585050350 100644 --- a/android/src/main/java/com/reactnativenavigation/hierarchy/root/RootAnimator.kt +++ b/android/src/main/java/com/reactnativenavigation/hierarchy/root/RootAnimator.kt @@ -10,6 +10,10 @@ import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController open class RootAnimator { open fun setRoot(appearing: ViewController<*>, disappearing: ViewController<*>?, setRoot: TransitionAnimationOptions, onAnimationEnd: ()->Unit) { + if (appearing.isDestroyed) { + onAnimationEnd() + return + } appearing.view.visibility = View.VISIBLE if (!setRoot.hasValue() || (!setRoot.enter.hasAnimation() && !setRoot.exit.hasAnimation())) { @@ -22,10 +26,9 @@ open class RootAnimator { val appearingAnimation = if (setRoot.enter.hasAnimation()) { setRoot.enter.getAnimation(appearing.view) } else null - val disappearingAnimation = if (disappearing != null && setRoot.exit.hasAnimation()) { + val disappearingAnimation = if (disappearing != null && !disappearing.isDestroyed && setRoot.exit.hasAnimation()) { setRoot.exit.getAnimation(disappearing.view) } else null - when { appearingAnimation != null && disappearingAnimation != null -> animationSet.playTogether(appearingAnimation, disappearingAnimation) appearingAnimation != null -> animationSet.play(appearingAnimation) diff --git a/android/src/main/java/com/reactnativenavigation/react/NavigationTurboModule.kt b/android/src/main/java/com/reactnativenavigation/react/NavigationTurboModule.kt index f02a70b8804..de36aa609a5 100644 --- a/android/src/main/java/com/reactnativenavigation/react/NavigationTurboModule.kt +++ b/android/src/main/java/com/reactnativenavigation/react/NavigationTurboModule.kt @@ -84,6 +84,11 @@ class NavigationTurboModule( handle { Log.d("NavigationTurboModule", "setRoot handle ${Thread.currentThread()}") val viewController = layoutFactory.create(layoutTree) + val activity = currentActivity + if (activity == null) { + promise.reject("ACTIVITY_NULL", "Activity is null") + return@handle + } navigator()?.setRoot( viewController, NativeCommandListener("setRoot", commandId, promise, eventEmitter, now) diff --git a/android/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java b/android/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java index 8a24a2e20f2..d0089c536ba 100644 --- a/android/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java +++ b/android/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java @@ -7,6 +7,7 @@ import com.reactnativenavigation.viewcontrollers.viewcontroller.Presenter; import com.reactnativenavigation.react.events.EventEmitter; import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry; +import com.reactnativenavigation.NavigationApplication; import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController; import com.reactnativenavigation.viewcontrollers.stack.topbar.TopBarController; @@ -32,7 +33,7 @@ public StackControllerBuilder(Activity activity, EventEmitter eventEmitter) { this.activity = activity; this.eventEmitter = eventEmitter; presenter = new Presenter(activity, new Options()); - animator = new StackAnimator(activity); + animator = new StackAnimator(activity != null ? activity : NavigationApplication.instance); } public StackControllerBuilder setEventEmitter(EventEmitter eventEmitter) { diff --git a/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/Presenter.java b/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/Presenter.java index f1e16de088b..a5197af4e55 100644 --- a/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/Presenter.java +++ b/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/Presenter.java @@ -65,7 +65,7 @@ public void onViewBroughtToFront(ViewController viewController, Options optio } private void applyOrientation(OrientationOptions options) { - activity.setRequestedOrientation(options.getValue()); + if (activity != null) activity.setRequestedOrientation(options.getValue()); } private void applyViewOptions(ViewController view, Options options) { @@ -127,6 +127,7 @@ private void mergeNavigationBarVisibility(NavigationBarOptions options) { } private void applyNavigationBarVisibility(NavigationBarOptions options) { + if (activity == null) return; View decorView = activity.getWindow().getDecorView(); if (options.isVisible.isTrueOrUndefined()) { SystemUiUtils.showNavigationBar(activity.getWindow(), decorView); diff --git a/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/RootPresenter.java b/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/RootPresenter.java index a09984ad5a5..1f13c8c23d6 100644 --- a/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/RootPresenter.java +++ b/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/RootPresenter.java @@ -36,6 +36,10 @@ public RootPresenter(RootAnimator animator, LayoutDirectionApplier layoutDirecti public void setRoot(ViewController appearingRoot, ViewController disappearingRoot, Options defaultOptions, CommandListener listener) { layoutDirectionApplier.apply(appearingRoot, defaultOptions); + if (appearingRoot.isDestroyed()) { + listener.onError("Could not set root - appearingRoot is already destroyed"); + return; + } rootLayout.addView(appearingRoot.getView(), matchParentWithBehaviour(new BehaviourDelegate(appearingRoot))); Options options = appearingRoot.resolveCurrentOptions(defaultOptions); AnimationOptions enter = options.animations.setRoot.getEnter(); @@ -60,6 +64,10 @@ private void animateSetRootAndReportSuccess(ViewController root, CommandListener listener, Options options) { + if (root.isDestroyed()) { + listener.onError("Could not set root - root is already destroyed"); + return; + } AnimationOptions exit = options.animations.setRoot.getExit(); AnimationOptions enter = options.animations.setRoot.getEnter(); if ((enter.enabled.isTrueOrUndefined() && enter.hasAnimation()) @@ -68,7 +76,9 @@ private void animateSetRootAndReportSuccess(ViewController root, disappearingRoot, options.animations.setRoot, () -> { - listener.onSuccess(root.getId()); + if (!root.isDestroyed()) { + listener.onSuccess(root.getId()); + } return Unit.INSTANCE; }); } else { diff --git a/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/ViewController.java b/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/ViewController.java index 0035533a5c5..2df966378c7 100644 --- a/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/ViewController.java +++ b/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/ViewController.java @@ -235,7 +235,7 @@ public void performOnParentStack(Func1 task) { public T getView() { if (view == null) { if (isDestroyed) { - throw new RuntimeException("Tried to create view after it has already been destroyed"); + throw new RuntimeException("Tried to create view for " + getClass().getSimpleName() + " (id: " + id + ") after it has already been destroyed"); } view = createView(); view.setOnHierarchyChangeListener(this);