Browse Source

Destroy pushed screen if back is pressed during push animation

Closes #3894
Guy Carmeli 6 years ago
parent
commit
156565b6ac

+ 30
- 2
lib/android/app/src/main/java/com/reactnativenavigation/anim/NavigationAnimator.java View File

16
 
16
 
17
 import java.util.Collection;
17
 import java.util.Collection;
18
 import java.util.Collections;
18
 import java.util.Collections;
19
+import java.util.HashMap;
19
 import java.util.List;
20
 import java.util.List;
21
+import java.util.Map;
20
 
22
 
21
 import static com.reactnativenavigation.utils.CollectionUtils.merge;
23
 import static com.reactnativenavigation.utils.CollectionUtils.merge;
22
 
24
 
24
 public class NavigationAnimator extends BaseAnimator {
26
 public class NavigationAnimator extends BaseAnimator {
25
 
27
 
26
     private final ElementTransitionManager transitionManager;
28
     private final ElementTransitionManager transitionManager;
29
+    private Map<View, Animator> runningPushAnimations = new HashMap<>();
27
 
30
 
28
     public NavigationAnimator(Context context, ElementTransitionManager transitionManager) {
31
     public NavigationAnimator(Context context, ElementTransitionManager transitionManager) {
29
         super(context);
32
         super(context);
41
         Collection<Animator> elementTransitions = transitionManager.createTransitions(transitions, fromElements, toElements);
44
         Collection<Animator> elementTransitions = transitionManager.createTransitions(transitions, fromElements, toElements);
42
         set.playTogether(merge(push.getChildAnimations(), elementTransitions));
45
         set.playTogether(merge(push.getChildAnimations(), elementTransitions));
43
         set.addListener(new AnimatorListenerAdapter() {
46
         set.addListener(new AnimatorListenerAdapter() {
47
+            private boolean isCancelled;
48
+
44
             @Override
49
             @Override
45
             public void onAnimationStart(Animator animation) {
50
             public void onAnimationStart(Animator animation) {
46
                 view.setAlpha(1);
51
                 view.setAlpha(1);
47
             }
52
             }
48
 
53
 
49
             @Override
54
             @Override
50
-            public void onAnimationEnd(Animator animation) {
55
+            public void onAnimationCancel(Animator animation) {
56
+                isCancelled = true;
57
+                runningPushAnimations.remove(view);
51
                 onAnimationEnd.run();
58
                 onAnimationEnd.run();
52
             }
59
             }
60
+
61
+            @Override
62
+            public void onAnimationEnd(Animator animation) {
63
+                if (!isCancelled) {
64
+                    runningPushAnimations.remove(view);
65
+                    onAnimationEnd.run();
66
+                }
67
+            }
53
         });
68
         });
69
+        runningPushAnimations.put(view, set);
54
         set.start();
70
         set.start();
55
     }
71
     }
56
 
72
 
57
     public void pop(View view, NestedAnimationsOptions pop, Runnable onAnimationEnd) {
73
     public void pop(View view, NestedAnimationsOptions pop, Runnable onAnimationEnd) {
74
+        if (runningPushAnimations.containsKey(view)) {
75
+            runningPushAnimations.get(view).cancel();
76
+            onAnimationEnd.run();
77
+            return;
78
+        }
58
         AnimatorSet set = pop.content.getAnimation(view, getDefaultPopAnimation(view));
79
         AnimatorSet set = pop.content.getAnimation(view, getDefaultPopAnimation(view));
59
         set.addListener(new AnimatorListenerAdapter() {
80
         set.addListener(new AnimatorListenerAdapter() {
81
+            private boolean cancelled;
82
+
83
+            @Override
84
+            public void onAnimationCancel(Animator animation) {
85
+                this.cancelled = true;
86
+            }
87
+
60
             @Override
88
             @Override
61
             public void onAnimationEnd(Animator animation) {
89
             public void onAnimationEnd(Animator animation) {
62
-                onAnimationEnd.run();
90
+                if (!cancelled) onAnimationEnd.run();
63
             }
91
             }
64
         });
92
         });
65
         set.start();
93
         set.start();

+ 4
- 4
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java View File

58
     private ViewVisibilityListener viewVisibilityListener = new ViewVisibilityListenerAdapter();
58
     private ViewVisibilityListener viewVisibilityListener = new ViewVisibilityListenerAdapter();
59
     protected FabOptionsPresenter fabOptionsPresenter;
59
     protected FabOptionsPresenter fabOptionsPresenter;
60
 
60
 
61
+    public boolean isDestroyed() {
62
+        return isDestroyed;
63
+    }
64
+
61
     public ViewController(Activity activity, String id, YellowBoxDelegate yellowBoxDelegate, Options initialOptions) {
65
     public ViewController(Activity activity, String id, YellowBoxDelegate yellowBoxDelegate, Options initialOptions) {
62
         this.activity = activity;
66
         this.activity = activity;
63
         this.id = id;
67
         this.id = id;
241
         }
245
         }
242
     }
246
     }
243
 
247
 
244
-    protected boolean isDestroyed() {
245
-        return isDestroyed;
246
-    }
247
-
248
     @Override
248
     @Override
249
     public void onGlobalLayout() {
249
     public void onGlobalLayout() {
250
         if (!isShown && isViewShown()) {
250
         if (!isShown && isViewShown()) {

+ 6
- 3
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java View File

149
                     }));
149
                     }));
150
                 } else {
150
                 } else {
151
                     animator.push(child.getView(), resolvedOptions.animations.push, () -> {
151
                     animator.push(child.getView(), resolvedOptions.animations.push, () -> {
152
-                        getView().removeView(toRemove.getView());
152
+                        if (!toRemove.equals(peek())) {
153
+                            getView().removeView(toRemove.getView());
154
+                        }
153
                         listener.onSuccess(child.getId());
155
                         listener.onSuccess(child.getId());
154
                     });
156
                     });
155
                 }
157
                 }
206
             appearingView.setLayoutParams(new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
208
             appearingView.setLayoutParams(new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
207
             presenter.applyLayoutParamsOptions(resolvedOptions, appearingView);
209
             presenter.applyLayoutParamsOptions(resolvedOptions, appearingView);
208
         }
210
         }
209
-        getView().addView(appearingView, 0);
211
+        if (appearingView.getParent() == null) {
212
+            getView().addView(appearingView, 0);
213
+        }
210
         presenter.onChildWillAppear(appearing.options, disappearing.options);
214
         presenter.onChildWillAppear(appearing.options, disappearing.options);
211
         if (disappearing.options.animations.pop.enable.isTrueOrUndefined()) {
215
         if (disappearing.options.animations.pop.enable.isTrueOrUndefined()) {
212
             animator.pop(disappearing.getView(), resolvedOptions.animations.pop, () -> finishPopping(disappearing, listener));
216
             animator.pop(disappearing.getView(), resolvedOptions.animations.pop, () -> finishPopping(disappearing, listener));
216
     }
220
     }
217
 
221
 
218
     private void finishPopping(ViewController disappearing, CommandListener listener) {
222
     private void finishPopping(ViewController disappearing, CommandListener listener) {
219
-        getView().removeView(disappearing.getView());
220
         disappearing.destroy();
223
         disappearing.destroy();
221
         listener.onSuccess(disappearing.getId());
224
         listener.onSuccess(disappearing.getId());
222
     }
225
     }

+ 20
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.java View File

30
 import com.reactnativenavigation.views.Component;
30
 import com.reactnativenavigation.views.Component;
31
 import com.reactnativenavigation.views.ReactComponent;
31
 import com.reactnativenavigation.views.ReactComponent;
32
 import com.reactnativenavigation.views.StackLayout;
32
 import com.reactnativenavigation.views.StackLayout;
33
+import com.reactnativenavigation.views.element.ElementTransitionManager;
33
 import com.reactnativenavigation.views.topbar.TopBar;
34
 import com.reactnativenavigation.views.topbar.TopBar;
34
 
35
 
35
 import org.assertj.core.api.iterable.Extractor;
36
 import org.assertj.core.api.iterable.Extractor;
71
     @Override
72
     @Override
72
     public void beforeEach() {
73
     public void beforeEach() {
73
         super.beforeEach();
74
         super.beforeEach();
74
-        animator = Mockito.mock(NavigationAnimator.class);
75
         backButtonHelper = spy(new BackButtonHelper());
75
         backButtonHelper = spy(new BackButtonHelper());
76
         activity = newActivity();
76
         activity = newActivity();
77
+        animator = spy(new NavigationAnimator(activity, Mockito.mock(ElementTransitionManager.class)));
77
         childRegistry = new ChildControllersRegistry();
78
         childRegistry = new ChildControllersRegistry();
78
         presenter = spy(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), ImageLoaderMock.mock(), new Options()));
79
         presenter = spy(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), ImageLoaderMock.mock(), new Options()));
79
         child1 = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
80
         child1 = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
168
         verify(animator, times(0)).push(eq(child1.getView()), eq(child1.options.animations.push), any());
169
         verify(animator, times(0)).push(eq(child1.getView()), eq(child1.options.animations.push), any());
169
     }
170
     }
170
 
171
 
172
+    @Test
173
+    public void push_backPressedDuringPushAnimationDestroysPushedScreenImmediately() {
174
+        disablePushAnimation(child1);
175
+        uut.push(child1, new CommandListenerAdapter());
176
+
177
+        CommandListenerAdapter pushListener = spy(new CommandListenerAdapter());
178
+        uut.push(child2, pushListener);
179
+        CommandListenerAdapter backListener = spy(new CommandListenerAdapter());
180
+        uut.handleBack(backListener);
181
+        assertThat(uut.size()).isOne();
182
+        assertThat(child1.getView().getParent()).isEqualTo(uut.getView());
183
+        assertThat(child2.isDestroyed()).isTrue();
184
+
185
+        InOrder inOrder = inOrder(pushListener, backListener);
186
+        inOrder.verify(pushListener).onSuccess(any());
187
+        inOrder.verify(backListener).onSuccess(any());
188
+    }
189
+
171
     @Test
190
     @Test
172
     public void animateSetRoot() {
191
     public void animateSetRoot() {
173
         disablePushAnimation(child1, child2, child3);
192
         disablePushAnimation(child1, child2, child3);