Browse Source

dismissInAppNotification support on android. (#687)

* Add .hideInAppNotification on android.

- Separate hide and show animations.
- Implement a little bit of throttling around successive calls to .showInAppNotification

* Update example to demo dismiss.
Royce Townsend 7 years ago
parent
commit
5135d298ad

+ 9
- 22
android/app/src/main/java/com/reactnativenavigation/animation/PeekingAnimator.java View File

1
 package com.reactnativenavigation.animation;
1
 package com.reactnativenavigation.animation;
2
 
2
 
3
 import android.animation.Animator;
3
 import android.animation.Animator;
4
-import android.animation.AnimatorSet;
5
 import android.animation.ObjectAnimator;
4
 import android.animation.ObjectAnimator;
6
 import android.view.View;
5
 import android.view.View;
7
 import android.view.animation.OvershootInterpolator;
6
 import android.view.animation.OvershootInterpolator;
12
 
11
 
13
     private static final int SLIDE_OUT_DURATION = 300;
12
     private static final int SLIDE_OUT_DURATION = 300;
14
     private static final int SLIDE_IN_DURATION = 600;
13
     private static final int SLIDE_IN_DURATION = 600;
15
-    private static final int SUSTAIN_DURATION = 3000;
16
 
14
 
17
     private final Animator animator;
15
     private final Animator animator;
18
 
16
 
19
-    public PeekingAnimator(View view) {
20
-        this.animator = createAnimator(view);
17
+    public PeekingAnimator(View view, final boolean show) {
18
+        final int heightPixels = view.getLayoutParams().height;
19
+
20
+        this.animator = show ?
21
+                createSlideInAnimator(view, heightPixels) :
22
+                createSlideOutAnimator(view, heightPixels);
21
     }
23
     }
22
 
24
 
23
     public void addListener(Animator.AnimatorListener listener) {
25
     public void addListener(Animator.AnimatorListener listener) {
28
         animator.start();
30
         animator.start();
29
     }
31
     }
30
 
32
 
31
-    private Animator createAnimator(View view) {
32
-        final int heightPixels = view.getLayoutParams().height;
33
-
33
+    private ObjectAnimator createSlideInAnimator(View view, int heightPixels) {
34
         view.setTranslationY(-heightPixels);
34
         view.setTranslationY(-heightPixels);
35
 
35
 
36
-        ObjectAnimator slideIn = createSlideInAnimator(view);
37
-        ObjectAnimator slideOut = createSlideOutAnimator(view, heightPixels, slideIn);
38
-        AnimatorSet animatorSet = createAnimatorSet(slideIn, slideOut);
39
-        return animatorSet;
40
-    }
41
-
42
-    private ObjectAnimator createSlideInAnimator(View view) {
43
         ObjectAnimator slideIn = ObjectAnimator.ofFloat(view, TRANSLATION_Y, 0);
36
         ObjectAnimator slideIn = ObjectAnimator.ofFloat(view, TRANSLATION_Y, 0);
44
         slideIn.setDuration(SLIDE_IN_DURATION);
37
         slideIn.setDuration(SLIDE_IN_DURATION);
45
         slideIn.setInterpolator(new OvershootInterpolator(0.8f));
38
         slideIn.setInterpolator(new OvershootInterpolator(0.8f));
46
         return slideIn;
39
         return slideIn;
47
     }
40
     }
48
 
41
 
49
-    private ObjectAnimator createSlideOutAnimator(View view, int heightPixels, ObjectAnimator slideIn) {
42
+    private ObjectAnimator createSlideOutAnimator(View view, int heightPixels) {
50
         ObjectAnimator slideOut = ObjectAnimator.ofFloat(view, TRANSLATION_Y, -heightPixels);
43
         ObjectAnimator slideOut = ObjectAnimator.ofFloat(view, TRANSLATION_Y, -heightPixels);
51
-        slideIn.setDuration(SLIDE_OUT_DURATION);
44
+        slideOut.setDuration(SLIDE_OUT_DURATION);
52
         return slideOut;
45
         return slideOut;
53
     }
46
     }
54
-
55
-    private AnimatorSet createAnimatorSet(ObjectAnimator slideIn, ObjectAnimator slideOut) {
56
-        AnimatorSet animatorSet = new AnimatorSet();
57
-        animatorSet.play(slideOut).after(SUSTAIN_DURATION).after(slideIn);
58
-        return animatorSet;
59
-    }
60
 }
47
 }

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/bridge/NavigationReactModule.java View File

193
         NavigationCommandsHandler.showSlidingOverlay(slidingOverlayParams);
193
         NavigationCommandsHandler.showSlidingOverlay(slidingOverlayParams);
194
     }
194
     }
195
 
195
 
196
+    @ReactMethod
197
+    public void hideSlidingOverlay(final ReadableMap params) {
198
+        NavigationCommandsHandler.hideSlidingOverlay();
199
+    }
200
+
196
     @ReactMethod
201
     @ReactMethod
197
     public void showSnackbar(final ReadableMap params) {
202
     public void showSnackbar(final ReadableMap params) {
198
         SnackbarParams snackbarParams = new SnackbarParamsParser().parse(BundleConverter.toBundle(params));
203
         SnackbarParams snackbarParams = new SnackbarParamsParser().parse(BundleConverter.toBundle(params));

+ 4
- 0
android/app/src/main/java/com/reactnativenavigation/controllers/NavigationActivity.java View File

296
         layout.showSlidingOverlay(params);
296
         layout.showSlidingOverlay(params);
297
     }
297
     }
298
 
298
 
299
+    public void hideSlidingOverlay() {
300
+        layout.hideSlidingOverlay();
301
+    }
302
+
299
     public void showSnackbar(SnackbarParams params) {
303
     public void showSnackbar(SnackbarParams params) {
300
         layout.showSnackbar(params);
304
         layout.showSnackbar(params);
301
     }
305
     }

+ 14
- 0
android/app/src/main/java/com/reactnativenavigation/controllers/NavigationCommandsHandler.java View File

351
         });
351
         });
352
     }
352
     }
353
 
353
 
354
+    public static void hideSlidingOverlay() {
355
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
356
+        if (currentActivity == null) {
357
+            return;
358
+        }
359
+
360
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
361
+            @Override
362
+            public void run() {
363
+                currentActivity.hideSlidingOverlay();
364
+            }
365
+        });
366
+    }
367
+
354
     public static void showSnackbar(final SnackbarParams params) {
368
     public static void showSnackbar(final SnackbarParams params) {
355
         final NavigationActivity currentActivity = NavigationActivity.currentActivity;
369
         final NavigationActivity currentActivity = NavigationActivity.currentActivity;
356
         if (currentActivity == null) {
370
         if (currentActivity == null) {

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/layouts/BottomTabsLayout.java View File

216
         slidingOverlaysQueue.add(new SlidingOverlay(this, params));
216
         slidingOverlaysQueue.add(new SlidingOverlay(this, params));
217
     }
217
     }
218
 
218
 
219
+    @Override
220
+    public void hideSlidingOverlay() {
221
+        slidingOverlaysQueue.remove();
222
+    }
223
+
219
     @Override
224
     @Override
220
     public void onModalDismissed() {
225
     public void onModalDismissed() {
221
         EventBus.instance.post(new ScreenChangedEvent(getCurrentScreenStack().peek().getScreenParams()));
226
         EventBus.instance.post(new ScreenChangedEvent(getCurrentScreenStack().peek().getScreenParams()));

+ 2
- 0
android/app/src/main/java/com/reactnativenavigation/layouts/Layout.java View File

39
 
39
 
40
     void showSlidingOverlay(SlidingOverlayParams params);
40
     void showSlidingOverlay(SlidingOverlayParams params);
41
 
41
 
42
+    void hideSlidingOverlay();
43
+
42
     void onModalDismissed();
44
     void onModalDismissed();
43
 
45
 
44
     boolean containsNavigator(String navigatorId);
46
     boolean containsNavigator(String navigatorId);

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/layouts/SingleScreenLayout.java View File

219
         slidingOverlaysQueue.add(new SlidingOverlay(this, params));
219
         slidingOverlaysQueue.add(new SlidingOverlay(this, params));
220
     }
220
     }
221
 
221
 
222
+    @Override
223
+    public void hideSlidingOverlay() {
224
+        slidingOverlaysQueue.remove();
225
+    }
226
+
222
     @Override
227
     @Override
223
     public void onModalDismissed() {
228
     public void onModalDismissed() {
224
         EventBus.instance.post(new ScreenChangedEvent(stack.peek().getScreenParams()));
229
         EventBus.instance.post(new ScreenChangedEvent(stack.peek().getScreenParams()));

+ 50
- 4
android/app/src/main/java/com/reactnativenavigation/views/slidingOverlay/SlidingOverlay.java View File

13
 
13
 
14
 public class SlidingOverlay {
14
 public class SlidingOverlay {
15
 
15
 
16
+    private enum VisibilityState {
17
+        Hidden, AnimateHide, Shown, AnimateShow
18
+    }
19
+
20
+    private final ContentView view;
16
     private final RelativeLayout parent;
21
     private final RelativeLayout parent;
17
     private final SlidingOverlayParams params;
22
     private final SlidingOverlayParams params;
18
 
23
 
19
     private SlidingListener listener;
24
     private SlidingListener listener;
25
+    private VisibilityState visibilityState = VisibilityState.Hidden;
20
 
26
 
21
     public interface SlidingListener {
27
     public interface SlidingListener {
22
         void onSlidingOverlayGone();
28
         void onSlidingOverlayGone();
29
+        void onSlidingOverlayShown();
23
     }
30
     }
24
 
31
 
25
     public SlidingOverlay(RelativeLayout parent, SlidingOverlayParams params) {
32
     public SlidingOverlay(RelativeLayout parent, SlidingOverlayParams params) {
26
         this.parent = parent;
33
         this.parent = parent;
27
         this.params = params;
34
         this.params = params;
35
+        view = createSlidingOverlayView(params);
28
     }
36
     }
29
 
37
 
30
     public void setSlidingListener(SlidingListener listener) {
38
     public void setSlidingListener(SlidingListener listener) {
32
     }
40
     }
33
 
41
 
34
     public void show() {
42
     public void show() {
35
-        final ContentView view = createSlidingOverlayView(params);
36
         parent.addView(view);
43
         parent.addView(view);
37
 
44
 
38
-        final PeekingAnimator animator = new PeekingAnimator(view);
45
+        final PeekingAnimator animator = new PeekingAnimator(view, true);
39
         animator.addListener(new AnimatorListenerAdapter() {
46
         animator.addListener(new AnimatorListenerAdapter() {
40
             @Override
47
             @Override
41
             public void onAnimationCancel(Animator animator) {
48
             public void onAnimationCancel(Animator animator) {
42
-                onSlidingOverlayEnd(view);
49
+                onSlidingOverlayShown(view);
43
             }
50
             }
44
 
51
 
45
             @Override
52
             @Override
46
             public void onAnimationEnd(Animator animator) {
53
             public void onAnimationEnd(Animator animator) {
47
-                onSlidingOverlayEnd(view);
54
+                onSlidingOverlayShown(view);
48
             }
55
             }
49
         });
56
         });
50
         view.setOnDisplayListener(new Screen.OnDisplayListener() {
57
         view.setOnDisplayListener(new Screen.OnDisplayListener() {
51
             @Override
58
             @Override
52
             public void onDisplay() {
59
             public void onDisplay() {
53
                 view.setVisibility(View.VISIBLE);
60
                 view.setVisibility(View.VISIBLE);
61
+                visibilityState = VisibilityState.AnimateShow;
54
                 animator.animate();
62
                 animator.animate();
55
             }
63
             }
56
         });
64
         });
57
     }
65
     }
58
 
66
 
67
+    public void hide() {
68
+        final PeekingAnimator animator = new PeekingAnimator(view, false);
69
+        animator.addListener(new AnimatorListenerAdapter() {
70
+            @Override
71
+            public void onAnimationCancel(Animator animator) {
72
+                onSlidingOverlayEnd(view);
73
+            }
74
+
75
+            @Override
76
+            public void onAnimationEnd(Animator animator) {
77
+                onSlidingOverlayEnd(view);
78
+            }
79
+        });
80
+
81
+        visibilityState = VisibilityState.AnimateHide;
82
+        animator.animate();
83
+    }
84
+
85
+    public boolean isShowing() {
86
+        return VisibilityState.AnimateShow == visibilityState;
87
+    }
88
+
89
+    public boolean isVisible() {
90
+        return VisibilityState.Shown == visibilityState;
91
+    }
92
+
93
+    public boolean isHiding() {
94
+        return VisibilityState.AnimateHide == visibilityState;
95
+    }
96
+
59
     protected ContentView createSlidingOverlayView(SlidingOverlayParams params) {
97
     protected ContentView createSlidingOverlayView(SlidingOverlayParams params) {
60
         final float heightPixels = ViewUtils.convertDpToPixel(100);
98
         final float heightPixels = ViewUtils.convertDpToPixel(100);
61
 
99
 
68
         return view;
106
         return view;
69
     }
107
     }
70
 
108
 
109
+    protected void onSlidingOverlayShown(ContentView view) {
110
+        visibilityState = VisibilityState.Shown;
111
+        if (listener != null) {
112
+            listener.onSlidingOverlayShown();
113
+        }
114
+    }
115
+
71
     protected void onSlidingOverlayEnd(ContentView view) {
116
     protected void onSlidingOverlayEnd(ContentView view) {
117
+        visibilityState = VisibilityState.Hidden;
72
         view.unmountReactView();
118
         view.unmountReactView();
73
         parent.removeView(view);
119
         parent.removeView(view);
74
 
120
 

+ 58
- 0
android/app/src/main/java/com/reactnativenavigation/views/slidingOverlay/SlidingOverlaysQueue.java View File

4
 
4
 
5
 import java.util.LinkedList;
5
 import java.util.LinkedList;
6
 import java.util.Queue;
6
 import java.util.Queue;
7
+import java.util.Timer;
8
+import java.util.TimerTask;
7
 
9
 
8
 public class SlidingOverlaysQueue implements SlidingOverlay.SlidingListener{
10
 public class SlidingOverlaysQueue implements SlidingOverlay.SlidingListener{
9
 
11
 
12
+    private static final int SUSTAIN_DURATION = 3000;
13
+    private static final int SHORT_SUSTAIN_DURATION = 500;
14
+
15
+    protected Timer autoDismissTimer = null;
16
+    protected boolean pendingHide;
10
     protected Queue<SlidingOverlay> queue = new LinkedList<>();
17
     protected Queue<SlidingOverlay> queue = new LinkedList<>();
11
 
18
 
12
     public void add(final SlidingOverlay slidingOverlay) {
19
     public void add(final SlidingOverlay slidingOverlay) {
17
                 if (queue.size() == 1) {
24
                 if (queue.size() == 1) {
18
                     dispatchNextSlidingOverlay();
25
                     dispatchNextSlidingOverlay();
19
                 }
26
                 }
27
+                else {
28
+                    SlidingOverlay currentOverlay = queue.peek();
29
+                    if (currentOverlay.isVisible()) {
30
+                        if (autoDismissTimer != null) {
31
+                            autoDismissTimer.cancel();
32
+                            autoDismissTimer = null;
33
+                        }
34
+                        currentOverlay.hide();
35
+                    }
36
+                }
20
             }
37
             }
21
         });
38
         });
22
     }
39
     }
23
 
40
 
41
+    public void remove() {
42
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
43
+            @Override
44
+            public void run() {
45
+                SlidingOverlay currentOverlay = queue.peek();
46
+
47
+                if (currentOverlay.isShowing()) {
48
+                    pendingHide = true;
49
+                }
50
+                else if (currentOverlay.isVisible()) {
51
+                    if (autoDismissTimer != null) {
52
+                        autoDismissTimer.cancel();
53
+                        autoDismissTimer = null;
54
+                    }
55
+                    currentOverlay.hide();
56
+                }
57
+            }
58
+        });
59
+    }
60
+
61
+    @Override
62
+    public void onSlidingOverlayShown() {
63
+        int autoDismissDuration = pendingHide || queue.size() > 1
64
+                ? SHORT_SUSTAIN_DURATION
65
+                : SUSTAIN_DURATION;
66
+        pendingHide = false;
67
+
68
+        autoDismissTimer = new Timer();
69
+        autoDismissTimer.schedule(new TimerTask() {
70
+            @Override
71
+            public void run() {
72
+                NavigationApplication.instance.runOnMainThread(new Runnable() {
73
+                    @Override
74
+                    public void run() {
75
+                        queue.peek().hide();
76
+                    }
77
+                });
78
+            }
79
+        }, autoDismissDuration);
80
+    }
81
+
24
     @Override
82
     @Override
25
     public void onSlidingOverlayGone() {
83
     public void onSlidingOverlayGone() {
26
         queue.poll();
84
         queue.poll();

+ 4
- 1
example/src/screens/InAppNotification.js View File

33
     return (
33
     return (
34
       <View style={styleSheet.container}>
34
       <View style={styleSheet.container}>
35
           <Text style={{color: 'black', fontSize: 20}}>{message}</Text>
35
           <Text style={{color: 'black', fontSize: 20}}>{message}</Text>
36
-          <TouchableHighlight onPress={() => Alert.alert(`You've tapped on notification #${this._counter}`, 'We hope you had fun!')}>
36
+          <TouchableHighlight onPress={() => {
37
+            this.props.navigator.dismissInAppNotification();
38
+            Alert.alert(`You've tapped on notification #${this._counter}`, 'We hope you had fun!');
39
+          }}>
37
             <Text>Tap Me</Text>
40
             <Text>Tap Me</Text>
38
           </TouchableHighlight>
41
           </TouchableHighlight>
39
       </View>
42
       </View>

+ 5
- 0
src/deprecated/platformSpecificDeprecated.android.js View File

381
   newPlatformSpecific.showInAppNotification(params);
381
   newPlatformSpecific.showInAppNotification(params);
382
 }
382
 }
383
 
383
 
384
+function dismissInAppNotification(params) {
385
+  newPlatformSpecific.dismissInAppNotification(params);
386
+}
387
+
384
 function addNavigatorParams(screen, navigator = null, idx = '') {
388
 function addNavigatorParams(screen, navigator = null, idx = '') {
385
   screen.navigatorID = navigator ? navigator.navigatorID : _.uniqueId('navigatorID') + '_nav' + idx;
389
   screen.navigatorID = navigator ? navigator.navigatorID : _.uniqueId('navigatorID') + '_nav' + idx;
386
   screen.screenInstanceID = _.uniqueId('screenInstanceID');
390
   screen.screenInstanceID = _.uniqueId('screenInstanceID');
576
   dismissModal,
580
   dismissModal,
577
   dismissAllModals,
581
   dismissAllModals,
578
   showInAppNotification,
582
   showInAppNotification,
583
+  dismissInAppNotification,
579
   navigatorSetButtons,
584
   navigatorSetButtons,
580
   navigatorSetTabBadge,
585
   navigatorSetTabBadge,
581
   navigatorSetTitle,
586
   navigatorSetTitle,

+ 5
- 0
src/platformSpecific.android.js View File

65
   NativeReactModule.showSlidingOverlay(params);
65
   NativeReactModule.showSlidingOverlay(params);
66
 }
66
 }
67
 
67
 
68
+function dismissInAppNotification(params) {
69
+  NativeReactModule.hideSlidingOverlay(params);
70
+}
71
+
68
 function savePassProps(params) {
72
 function savePassProps(params) {
69
   if (params.navigationParams && params.passProps) {
73
   if (params.navigationParams && params.passProps) {
70
     PropRegistry.save(params.navigationParams.screenInstanceID, params.passProps);
74
     PropRegistry.save(params.navigationParams.screenInstanceID, params.passProps);
150
   dismissTopModal,
154
   dismissTopModal,
151
   dismissAllModals,
155
   dismissAllModals,
152
   showInAppNotification,
156
   showInAppNotification,
157
+  dismissInAppNotification,
153
   toggleSideMenuVisible,
158
   toggleSideMenuVisible,
154
   setSideMenuVisible,
159
   setSideMenuVisible,
155
   selectBottomTabByNavigatorId,
160
   selectBottomTabByNavigatorId,