Skip to content

Commit

Permalink
Cancel showModal animation if back was pressed during animation (#6462)
Browse files Browse the repository at this point in the history
closes #5245
  • Loading branch information
guyca authored Aug 10, 2020
1 parent f8cdb46 commit 58f72f8
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.reactnativenavigation.utils

open class ScreenAnimationListener {
open fun onStart() {}

open fun onEnd() {}

open fun onCancel() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,80 @@
import android.content.Context;
import android.view.View;

import com.reactnativenavigation.viewcontrollers.common.BaseAnimator;
import com.reactnativenavigation.options.AnimationOptions;
import com.reactnativenavigation.utils.ScreenAnimationListener;
import com.reactnativenavigation.viewcontrollers.common.BaseAnimator;

import java.util.HashMap;
import java.util.Map;

import static java.util.Objects.requireNonNull;

public class ModalAnimator extends BaseAnimator {

private Animator animator;
private Map<View, Animator> runningAnimators = new HashMap<>();

public ModalAnimator(Context context) {
super(context);
}

public void show(View view, AnimationOptions show, AnimatorListenerAdapter listener) {
animator = show.getAnimation(view, getDefaultPushAnimation(view));
animator.addListener(listener);
public void show(View view, AnimationOptions show, ScreenAnimationListener listener) {
Animator animator = show.getAnimation(view, getDefaultPushAnimation(view));
animator.addListener(new AnimatorListenerAdapter() {
private boolean isCancelled = false;

@Override
public void onAnimationStart(Animator animation) {
runningAnimators.put(view, animator);
listener.onStart();
}

@Override
public void onAnimationCancel(Animator animation) {
isCancelled = true;
listener.onCancel();
}

@Override
public void onAnimationEnd(Animator animation) {
runningAnimators.remove(view);
if (!isCancelled) listener.onEnd();
}
});
animator.start();
}

public void dismiss(View view, AnimationOptions dismiss, AnimatorListenerAdapter listener) {
animator = dismiss.getAnimation(view, getDefaultPopAnimation(view));
animator.addListener(listener);
public void dismiss(View view, AnimationOptions dismiss, ScreenAnimationListener listener) {
if (runningAnimators.containsKey(view)) {
requireNonNull(runningAnimators.get(view)).cancel();
listener.onEnd();
return;
}
Animator animator = dismiss.getAnimation(view, getDefaultPopAnimation(view));
animator.addListener(new AnimatorListenerAdapter() {
private boolean isCancelled = false;

@Override
public void onAnimationStart(Animator animation) {
listener.onStart();
}

@Override
public void onAnimationCancel(Animator animation) {
isCancelled = true;
listener.onCancel();
}

@Override
public void onAnimationEnd(Animator animation) {
runningAnimators.remove(view);
if (!isCancelled) listener.onEnd();
}
});
animator.start();
}

public boolean isRunning() {
return animator != null && animator.isRunning();
return !runningAnimators.isEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package com.reactnativenavigation.viewcontrollers.modal;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.view.View;
import android.view.ViewGroup;

import com.reactnativenavigation.options.ModalPresentationStyle;
import com.reactnativenavigation.options.Options;
import com.reactnativenavigation.react.CommandListener;
import com.reactnativenavigation.utils.ScreenAnimationListener;
import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController;

import androidx.annotation.Nullable;
Expand Down Expand Up @@ -66,16 +65,21 @@ void showModal(ViewController toAdd, ViewController toRemove, CommandListener li
}

private void animateShow(ViewController toAdd, ViewController toRemove, CommandListener listener, Options options) {
animator.show(toAdd.getView(), options.animations.showModal, new AnimatorListenerAdapter() {
animator.show(toAdd.getView(), options.animations.showModal, new ScreenAnimationListener() {
@Override
public void onAnimationStart(Animator animation) {
public void onStart() {
toAdd.getView().setAlpha(1);
}

@Override
public void onAnimationEnd(Animator animation) {
public void onEnd() {
onShowModalEnd(toAdd, toRemove, listener);
}

@Override
public void onCancel() {
listener.onSuccess(toAdd.getId());
}
});
}

Expand All @@ -98,9 +102,9 @@ void dismissModal(ViewController toDismiss, @Nullable ViewController toAdd, View
}
Options options = toDismiss.resolveCurrentOptions(defaultOptions);
if (options.animations.dismissModal.enabled.isTrueOrUndefined()) {
animator.dismiss(toDismiss.getView(), options.animations.dismissModal, new AnimatorListenerAdapter() {
animator.dismiss(toDismiss.getView(), options.animations.dismissModal, new ScreenAnimationListener() {
@Override
public void onAnimationEnd(Animator animation) {
public void onEnd() {
onDismissEnd(toDismiss, listener);
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.reactnativenavigation.viewcontrollers.modal;

import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.view.View;

import com.reactnativenavigation.options.AnimationOptions;
import com.reactnativenavigation.utils.ScreenAnimationListener;

public class ModalAnimatorMock extends ModalAnimator {

Expand All @@ -13,22 +13,22 @@ public class ModalAnimatorMock extends ModalAnimator {
}

@Override
public void show(View view, AnimationOptions show, AnimatorListenerAdapter listener) {
public void show(View view, AnimationOptions show, ScreenAnimationListener listener) {
try {
listener.onAnimationStart(null);
listener.onStart();
Thread.sleep(10);
listener.onAnimationEnd(null);
listener.onEnd();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}

@Override
public void dismiss(View view, AnimationOptions dismiss, AnimatorListenerAdapter listener) {
public void dismiss(View view, AnimationOptions dismiss, ScreenAnimationListener listener) {
try {
listener.onAnimationStart(null);
listener.onStart();
Thread.sleep(10);
listener.onAnimationEnd(null);
listener.onEnd();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.reactnativenavigation.viewcontrollers.modal

import com.nhaarman.mockitokotlin2.*
import com.reactnativenavigation.BaseTest
import com.reactnativenavigation.mocks.SimpleViewController
import com.reactnativenavigation.options.AnimationOptions
import com.reactnativenavigation.options.Options
import com.reactnativenavigation.utils.ScreenAnimationListener
import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry
import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController
import org.assertj.core.api.Java6Assertions.assertThat
import org.junit.Test

class ModalAnimatorTest : BaseTest() {
private lateinit var uut: ModalAnimator;
private lateinit var modal1: ViewController<*>

override fun beforeEach() {
val activity = newActivity()
val childRegistry = mock<ChildControllersRegistry>()
uut = ModalAnimator(activity)
modal1 = SimpleViewController(activity, childRegistry, "child1", Options())
}

@Test
fun show_isRunning() {
uut.show(modal1.view, AnimationOptions(), object : ScreenAnimationListener() {})
assertThat(uut.isRunning).isTrue()
}

@Test
fun dismiss_dismissModalDuringShowAnimation() {
val showListener = spy<ScreenAnimationListener>()
uut.show(modal1.view, AnimationOptions(), showListener)

val dismissListener = spy<ScreenAnimationListener>()
uut.dismiss(modal1.view, AnimationOptions(), dismissListener)

verify(showListener, times(1)).onCancel()
verify(showListener, never()).onEnd()
verify(dismissListener, times(1)).onEnd()
assertThat(uut.isRunning).isFalse()
}
}

0 comments on commit 58f72f8

Please sign in to comment.