Daniel Zlotin 6 år sedan
förälder
incheckning
9d18904db7

+ 63
- 0
lib/android/app/src/main/java/com/reactnativenavigation/anim/BaseAnimator.java Visa fil

1
+package com.reactnativenavigation.anim;
2
+
3
+
4
+import android.animation.AnimatorSet;
5
+import android.animation.ObjectAnimator;
6
+import android.content.Context;
7
+import android.support.annotation.NonNull;
8
+import android.support.annotation.RestrictTo;
9
+import android.view.View;
10
+import android.view.animation.AccelerateInterpolator;
11
+import android.view.animation.DecelerateInterpolator;
12
+
13
+import com.reactnativenavigation.parse.AnimationsOptions;
14
+import com.reactnativenavigation.utils.UiUtils;
15
+
16
+class BaseAnimator {
17
+
18
+    AnimationsOptions options = new AnimationsOptions();
19
+
20
+    private static final int DURATION = 300;
21
+    private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
22
+    private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
23
+
24
+    private float translationY;
25
+
26
+    BaseAnimator(Context context) {
27
+        translationY = UiUtils.getWindowHeight(context);
28
+    }
29
+
30
+    @NonNull
31
+    AnimatorSet getDefaultPushAnimation(View view) {
32
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1);
33
+        alpha.setInterpolator(DECELERATE_INTERPOLATOR);
34
+
35
+        AnimatorSet set = new AnimatorSet();
36
+        ObjectAnimator translationY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, this.translationY, 0);
37
+        translationY.setInterpolator(DECELERATE_INTERPOLATOR);
38
+        translationY.setDuration(DURATION);
39
+        alpha.setDuration(DURATION);
40
+        set.playTogether(translationY, alpha);
41
+        return set;
42
+    }
43
+
44
+
45
+    @NonNull
46
+    AnimatorSet getDefaultPopAnimation(View view) {
47
+        AnimatorSet set = new AnimatorSet();
48
+
49
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0);
50
+        alpha.setInterpolator(ACCELERATE_INTERPOLATOR);
51
+
52
+        ObjectAnimator translationY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, 0, this.translationY);
53
+        translationY.setInterpolator(ACCELERATE_INTERPOLATOR);
54
+        translationY.setDuration(DURATION);
55
+        alpha.setDuration(DURATION);
56
+        set.playTogether(translationY, alpha);
57
+        return set;
58
+    }
59
+
60
+    public void setOptions(AnimationsOptions options) {
61
+        this.options = options;
62
+    }
63
+}

+ 40
- 0
lib/android/app/src/main/java/com/reactnativenavigation/anim/ModalAnimator.java Visa fil

1
+package com.reactnativenavigation.anim;
2
+
3
+
4
+import android.animation.AnimatorListenerAdapter;
5
+import android.animation.AnimatorSet;
6
+import android.content.Context;
7
+import android.view.View;
8
+
9
+import com.reactnativenavigation.parse.AnimationOptions;
10
+import com.reactnativenavigation.parse.AnimationsOptions;
11
+
12
+public class ModalAnimator extends BaseAnimator {
13
+
14
+    public ModalAnimator(Context context, AnimationsOptions options) {
15
+        super(context);
16
+        this.options = options;
17
+    }
18
+
19
+    public void animateShow(View contentView, AnimatorListenerAdapter listener) {
20
+        AnimatorSet animatorSet;
21
+        if (options.showModal.hasValue()) {
22
+            animatorSet = options.showModal.getAnimation(contentView);
23
+        } else {
24
+            animatorSet = getDefaultPushAnimation(contentView);
25
+        }
26
+        animatorSet.addListener(listener);
27
+        animatorSet.start();
28
+    }
29
+
30
+    public void animateDismiss(View contentView, AnimatorListenerAdapter listener) {
31
+        AnimatorSet animatorSet;
32
+        if (options.dismissModal.hasValue()) {
33
+            animatorSet = options.dismissModal.getAnimation(contentView);
34
+        } else {
35
+            animatorSet = getDefaultPopAnimation(contentView);
36
+        }
37
+        animatorSet.addListener(listener);
38
+        animatorSet.start();
39
+    }
40
+}

+ 2
- 42
lib/android/app/src/main/java/com/reactnativenavigation/anim/NavigationAnimator.java Visa fil

15
 import com.reactnativenavigation.utils.UiUtils;
15
 import com.reactnativenavigation.utils.UiUtils;
16
 
16
 
17
 @SuppressWarnings("ResourceType")
17
 @SuppressWarnings("ResourceType")
18
-public class NavigationAnimator {
19
-
20
-    private AnimationsOptions options = new AnimationsOptions();
21
-
22
-    private static final int DURATION = 300;
23
-    private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
24
-    private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
25
-    private float translationY;
18
+public class NavigationAnimator extends BaseAnimator {
26
 
19
 
27
     public NavigationAnimator(Context context) {
20
     public NavigationAnimator(Context context) {
28
-        translationY = UiUtils.getWindowHeight(context);
21
+        super(context);
29
     }
22
     }
30
 
23
 
31
     public void animatePush(final View view, @Nullable final AnimationListener animationListener) {
24
     public void animatePush(final View view, @Nullable final AnimationListener animationListener) {
52
         set.start();
45
         set.start();
53
     }
46
     }
54
 
47
 
55
-    @NonNull
56
-    private AnimatorSet getDefaultPushAnimation(View view) {
57
-        ObjectAnimator alpha = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1);
58
-        alpha.setInterpolator(DECELERATE_INTERPOLATOR);
59
-
60
-        AnimatorSet set = new AnimatorSet();
61
-        ObjectAnimator translationY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, this.translationY, 0);
62
-        translationY.setInterpolator(DECELERATE_INTERPOLATOR);
63
-        translationY.setDuration(DURATION);
64
-        alpha.setDuration(DURATION);
65
-        set.playTogether(translationY, alpha);
66
-        return set;
67
-    }
68
-
69
     public void animatePop(View view, @Nullable final AnimationListener animationListener) {
48
     public void animatePop(View view, @Nullable final AnimationListener animationListener) {
70
         AnimatorSet set;
49
         AnimatorSet set;
71
         if (options.pop.hasValue()) {
50
         if (options.pop.hasValue()) {
83
         });
62
         });
84
         set.start();
63
         set.start();
85
     }
64
     }
86
-
87
-    @NonNull
88
-    private AnimatorSet getDefaultPopAnimation(View view) {
89
-        AnimatorSet set = new AnimatorSet();
90
-
91
-        ObjectAnimator alpha = ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0);
92
-        alpha.setInterpolator(ACCELERATE_INTERPOLATOR);
93
-
94
-        ObjectAnimator translationY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, 0, this.translationY);
95
-        translationY.setInterpolator(ACCELERATE_INTERPOLATOR);
96
-        translationY.setDuration(DURATION);
97
-        alpha.setDuration(DURATION);
98
-        set.playTogether(translationY, alpha);
99
-        return set;
100
-    }
101
-
102
-    public void setOptions(AnimationsOptions options) {
103
-        this.options = options;
104
-    }
105
 }
65
 }

+ 1
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/AnimationOptions.java Visa fil

3
 
3
 
4
 import android.animation.Animator;
4
 import android.animation.Animator;
5
 import android.animation.AnimatorSet;
5
 import android.animation.AnimatorSet;
6
+import android.util.Log;
6
 import android.util.Property;
7
 import android.util.Property;
7
 import android.view.View;
8
 import android.view.View;
8
 
9
 

+ 2
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/AnimationsOptions.java Visa fil

1
 package com.reactnativenavigation.parse;
1
 package com.reactnativenavigation.parse;
2
 
2
 
3
 
3
 
4
+import android.util.Log;
5
+
4
 import org.json.JSONObject;
6
 import org.json.JSONObject;
5
 
7
 
6
 public class AnimationsOptions {
8
 public class AnimationsOptions {

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java Visa fil

17
     @NonNull
17
     @NonNull
18
     public static Options parse(TypefaceLoader typefaceManager, JSONObject json, @NonNull Options defaultOptions) {
18
     public static Options parse(TypefaceLoader typefaceManager, JSONObject json, @NonNull Options defaultOptions) {
19
         Options result = new Options();
19
         Options result = new Options();
20
-        if (json == null) return result;
20
+        if (json == null) return result.withDefaultOptions(defaultOptions);
21
 
21
 
22
         result.orientationOptions = OrientationOptions.parse(json);
22
         result.orientationOptions = OrientationOptions.parse(json);
23
         result.topBarOptions = TopBarOptions.parse(typefaceManager, json.optJSONObject("topBar"));
23
         result.topBarOptions = TopBarOptions.parse(typefaceManager, json.optJSONObject("topBar"));

+ 42
- 34
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ModalStack.java Visa fil

14
 
14
 
15
 class ModalStack implements ModalListener {
15
 class ModalStack implements ModalListener {
16
 
16
 
17
-	private List<Modal> modals = new ArrayList<>();
17
+    private List<Modal> modals = new ArrayList<>();
18
     private ModalCreator creator;
18
     private ModalCreator creator;
19
     private ModalListener modalListener;
19
     private ModalListener modalListener;
20
 
20
 
26
     void showModal(final ViewController viewController, Promise promise) {
26
     void showModal(final ViewController viewController, Promise promise) {
27
         Modal modal = creator.create(viewController, this);
27
         Modal modal = creator.create(viewController, this);
28
         modals.add(modal);
28
         modals.add(modal);
29
-		modal.show();
29
+        modal.show();
30
         promise.resolve(viewController.getId());
30
         promise.resolve(viewController.getId());
31
-	}
32
-
33
-	void dismissModal(final String componentId, Promise promise) {
34
-		Modal modal = findModalByComponentId(componentId);
35
-		if (modal != null) {
36
-			modal.dismiss(promise);
37
-		} else {
38
-			Navigator.rejectPromise(promise);
39
-		}
40
-	}
41
-
42
-	void dismissAll(Promise promise) {
43
-		for (Modal modal : modals) {
44
-			modal.dismiss(size() == 1 ? promise : new NoOpPromise());
45
-		}
46
-		modals.clear();
47
-	}
31
+    }
32
+
33
+    void dismissModal(final String componentId, Promise promise) {
34
+        applyOnModal(componentId, (modal) -> modal.dismiss(promise), () -> Navigator.rejectPromise(promise));
35
+    }
36
+
37
+    void dismissAll(Promise promise) {
38
+        for (Modal modal : modals) {
39
+            modal.dismiss(size() == 1 ? promise : new NoOpPromise());
40
+        }
41
+        modals.clear();
42
+    }
48
 
43
 
49
     boolean isEmpty() {
44
     boolean isEmpty() {
50
         return modals.isEmpty();
45
         return modals.isEmpty();
54
         return modals.size();
49
         return modals.size();
55
     }
50
     }
56
 
51
 
57
-	@Nullable
52
+    @Nullable
58
     Modal findModalByComponentId(String componentId) {
53
     Modal findModalByComponentId(String componentId) {
59
-		for (Modal modal : modals) {
60
-			if (modal.containsDeepComponentId(componentId)) {
61
-				return modal;
62
-			}
63
-		}
64
-		return null;
65
-	}
66
-
67
-	@Nullable
54
+        for (Modal modal : modals) {
55
+            if (modal.containsDeepComponentId(componentId)) {
56
+                return modal;
57
+            }
58
+        }
59
+        return null;
60
+    }
61
+
62
+    @Nullable
68
     ViewController findControllerById(String id) {
63
     ViewController findControllerById(String id) {
69
         Modal modal = findModalByComponentId(id);
64
         Modal modal = findModalByComponentId(id);
70
         return modal != null ? modal.viewController.findControllerById(id) : null;
65
         return modal != null ? modal.viewController.findControllerById(id) : null;
74
     public void onModalDismiss(Modal modal) {
69
     public void onModalDismiss(Modal modal) {
75
         if (peek() == modal) {
70
         if (peek() == modal) {
76
             modals.remove(modal);
71
             modals.remove(modal);
77
-            performOnModal(peek(), peek -> peek.viewController.onViewAppeared());
72
+            applyOnModal(peek(), peek -> peek.viewController.onViewAppeared(), null);
78
         } else {
73
         } else {
79
             modals.remove(modal);
74
             modals.remove(modal);
80
         }
75
         }
90
         return isEmpty() ? null : modals.get(modals.size() - 1);
85
         return isEmpty() ? null : modals.get(modals.size() - 1);
91
     }
86
     }
92
 
87
 
93
-    private void performOnModal(@Nullable Modal modal, Task<Modal> task) {
94
-        if (modal != null) task.run(modal);
95
-    }
96
-
97
     public boolean handleBack() {
88
     public boolean handleBack() {
98
         return !modals.isEmpty() && peek().handleBack();
89
         return !modals.isEmpty() && peek().handleBack();
99
     }
90
     }
91
+
92
+    private void applyOnModal(String componentId, Task<Modal> accept, Runnable reject) {
93
+        Modal modal = findModalByComponentId(componentId);
94
+        if (modal != null) {
95
+            if (accept != null) accept.run(modal);
96
+        } else {
97
+            if (reject != null) reject.run();
98
+        }
99
+    }
100
+
101
+    private void applyOnModal(Modal modal, Task<Modal> accept, Runnable reject) {
102
+        if (modal != null) {
103
+            if (accept != null) accept.run(modal);
104
+        } else {
105
+            if (reject != null) reject.run();
106
+        }
107
+    }
100
 }
108
 }

+ 19
- 2
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/Modal.java Visa fil

1
 package com.reactnativenavigation.viewcontrollers.modal;
1
 package com.reactnativenavigation.viewcontrollers.modal;
2
 
2
 
3
+import android.animation.Animator;
4
+import android.animation.AnimatorListenerAdapter;
3
 import android.app.Dialog;
5
 import android.app.Dialog;
4
 import android.content.DialogInterface;
6
 import android.content.DialogInterface;
5
 import android.support.annotation.Nullable;
7
 import android.support.annotation.Nullable;
8
 
10
 
9
 import com.facebook.react.bridge.Promise;
11
 import com.facebook.react.bridge.Promise;
10
 import com.reactnativenavigation.R;
12
 import com.reactnativenavigation.R;
13
+import com.reactnativenavigation.anim.ModalAnimator;
11
 import com.reactnativenavigation.viewcontrollers.ViewController;
14
 import com.reactnativenavigation.viewcontrollers.ViewController;
12
 
15
 
13
 import static android.view.View.MeasureSpec.EXACTLY;
16
 import static android.view.View.MeasureSpec.EXACTLY;
19
     private ModalListener modalListener;
22
     private ModalListener modalListener;
20
     @Nullable private Promise dismissPromise;
23
     @Nullable private Promise dismissPromise;
21
 
24
 
25
+    private ModalAnimator animator;
26
+
22
     public Modal(final ViewController viewController, ModalListener modalListener) {
27
     public Modal(final ViewController viewController, ModalListener modalListener) {
23
         this.viewController = viewController;
28
         this.viewController = viewController;
24
         dialog = new Dialog(viewController.getActivity(), R.style.Modal);
29
         dialog = new Dialog(viewController.getActivity(), R.style.Modal);
26
         dialog.setOnKeyListener(this);
31
         dialog.setOnKeyListener(this);
27
         dialog.setOnDismissListener(this);
32
         dialog.setOnDismissListener(this);
28
         dialog.setOnShowListener(this);
33
         dialog.setOnShowListener(this);
34
+        animator = new ModalAnimator(viewController.getActivity(), viewController.options.animationsOptions);
29
     }
35
     }
30
 
36
 
31
     public void show() {
37
     public void show() {
32
         preMeasureView();
38
         preMeasureView();
33
-        dialog.setContentView(viewController.getView());
39
+        final View contentView = viewController.getView();
34
         dialog.show();
40
         dialog.show();
41
+        animator.animateShow(contentView, new AnimatorListenerAdapter() {
42
+            @Override
43
+            public void onAnimationStart(Animator animation) {
44
+                dialog.setContentView(contentView);
45
+            }
46
+        });
35
     }
47
     }
36
 
48
 
37
     public void dismiss(Promise promise) {
49
     public void dismiss(Promise promise) {
38
         dismissPromise = promise;
50
         dismissPromise = promise;
39
-        dialog.dismiss();
51
+        animator.animateDismiss(viewController.getView(), new AnimatorListenerAdapter() {
52
+            @Override
53
+            public void onAnimationEnd(Animator animation) {
54
+                dialog.dismiss();
55
+            }
56
+        });
40
     }
57
     }
41
 
58
 
42
     public boolean containsDeepComponentId(String componentId) {
59
     public boolean containsDeepComponentId(String componentId) {

+ 3
- 2
lib/android/app/src/main/res/values/styles.xml Visa fil

3
 
3
 
4
     <style name="Modal" parent="Theme.AppCompat.Light.NoActionBar">
4
     <style name="Modal" parent="Theme.AppCompat.Light.NoActionBar">
5
         <item name="android:windowAnimationStyle">@style/modalAnimations</item>
5
         <item name="android:windowAnimationStyle">@style/modalAnimations</item>
6
+        <item name="android:windowBackground">@android:color/transparent</item>
6
     </style>
7
     </style>
7
 
8
 
8
     <style name="modalAnimations">
9
     <style name="modalAnimations">
9
-        <item name="android:windowEnterAnimation">@anim/slide_up</item>
10
-        <item name="android:windowExitAnimation">@anim/slide_down</item>
10
+        <item name="android:windowEnterAnimation">@null</item>
11
+        <item name="android:windowExitAnimation">@null</item>
11
     </style>
12
     </style>
12
 </resources>
13
 </resources>

+ 6
- 2
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ModalStackTest.java Visa fil

103
     public void onDismiss() throws Exception {
103
     public void onDismiss() throws Exception {
104
         uut.showModal(viewController, new MockPromise());
104
         uut.showModal(viewController, new MockPromise());
105
         uut.showModal(new SimpleViewController(newActivity(), "otherComponent", new Options()), new MockPromise());
105
         uut.showModal(new SimpleViewController(newActivity(), "otherComponent", new Options()), new MockPromise());
106
-        uut.dismissAll(new MockPromise());
107
-        verify(uut, times(2)).onModalDismiss(any());
106
+        uut.dismissAll(new MockPromise() {
107
+            @Override
108
+            public void resolve(@Nullable Object value) {
109
+                verify(uut, times(2)).onModalDismiss(any());
110
+            }
111
+        });
108
     }
112
     }
109
 
113
 
110
     @Test
114
     @Test