Przeglądaj źródła

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 lat temu
rodzic
commit
5135d298ad

+ 9
- 22
android/app/src/main/java/com/reactnativenavigation/animation/PeekingAnimator.java Wyświetl plik

@@ -1,7 +1,6 @@
1 1
 package com.reactnativenavigation.animation;
2 2
 
3 3
 import android.animation.Animator;
4
-import android.animation.AnimatorSet;
5 4
 import android.animation.ObjectAnimator;
6 5
 import android.view.View;
7 6
 import android.view.animation.OvershootInterpolator;
@@ -12,12 +11,15 @@ public class PeekingAnimator {
12 11
 
13 12
     private static final int SLIDE_OUT_DURATION = 300;
14 13
     private static final int SLIDE_IN_DURATION = 600;
15
-    private static final int SUSTAIN_DURATION = 3000;
16 14
 
17 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 25
     public void addListener(Animator.AnimatorListener listener) {
@@ -28,33 +30,18 @@ public class PeekingAnimator {
28 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 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 36
         ObjectAnimator slideIn = ObjectAnimator.ofFloat(view, TRANSLATION_Y, 0);
44 37
         slideIn.setDuration(SLIDE_IN_DURATION);
45 38
         slideIn.setInterpolator(new OvershootInterpolator(0.8f));
46 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 43
         ObjectAnimator slideOut = ObjectAnimator.ofFloat(view, TRANSLATION_Y, -heightPixels);
51
-        slideIn.setDuration(SLIDE_OUT_DURATION);
44
+        slideOut.setDuration(SLIDE_OUT_DURATION);
52 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 Wyświetl plik

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

+ 4
- 0
android/app/src/main/java/com/reactnativenavigation/controllers/NavigationActivity.java Wyświetl plik

@@ -296,6 +296,10 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard
296 296
         layout.showSlidingOverlay(params);
297 297
     }
298 298
 
299
+    public void hideSlidingOverlay() {
300
+        layout.hideSlidingOverlay();
301
+    }
302
+
299 303
     public void showSnackbar(SnackbarParams params) {
300 304
         layout.showSnackbar(params);
301 305
     }

+ 14
- 0
android/app/src/main/java/com/reactnativenavigation/controllers/NavigationCommandsHandler.java Wyświetl plik

@@ -351,6 +351,20 @@ public class NavigationCommandsHandler {
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 368
     public static void showSnackbar(final SnackbarParams params) {
355 369
         final NavigationActivity currentActivity = NavigationActivity.currentActivity;
356 370
         if (currentActivity == null) {

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/layouts/BottomTabsLayout.java Wyświetl plik

@@ -216,6 +216,11 @@ public class BottomTabsLayout extends BaseLayout implements AHBottomNavigation.O
216 216
         slidingOverlaysQueue.add(new SlidingOverlay(this, params));
217 217
     }
218 218
 
219
+    @Override
220
+    public void hideSlidingOverlay() {
221
+        slidingOverlaysQueue.remove();
222
+    }
223
+
219 224
     @Override
220 225
     public void onModalDismissed() {
221 226
         EventBus.instance.post(new ScreenChangedEvent(getCurrentScreenStack().peek().getScreenParams()));

+ 2
- 0
android/app/src/main/java/com/reactnativenavigation/layouts/Layout.java Wyświetl plik

@@ -39,6 +39,8 @@ public interface Layout extends ScreenStackContainer {
39 39
 
40 40
     void showSlidingOverlay(SlidingOverlayParams params);
41 41
 
42
+    void hideSlidingOverlay();
43
+
42 44
     void onModalDismissed();
43 45
 
44 46
     boolean containsNavigator(String navigatorId);

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/layouts/SingleScreenLayout.java Wyświetl plik

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

+ 50
- 4
android/app/src/main/java/com/reactnativenavigation/views/slidingOverlay/SlidingOverlay.java Wyświetl plik

@@ -13,18 +13,26 @@ import com.reactnativenavigation.views.ContentView;
13 13
 
14 14
 public class SlidingOverlay {
15 15
 
16
+    private enum VisibilityState {
17
+        Hidden, AnimateHide, Shown, AnimateShow
18
+    }
19
+
20
+    private final ContentView view;
16 21
     private final RelativeLayout parent;
17 22
     private final SlidingOverlayParams params;
18 23
 
19 24
     private SlidingListener listener;
25
+    private VisibilityState visibilityState = VisibilityState.Hidden;
20 26
 
21 27
     public interface SlidingListener {
22 28
         void onSlidingOverlayGone();
29
+        void onSlidingOverlayShown();
23 30
     }
24 31
 
25 32
     public SlidingOverlay(RelativeLayout parent, SlidingOverlayParams params) {
26 33
         this.parent = parent;
27 34
         this.params = params;
35
+        view = createSlidingOverlayView(params);
28 36
     }
29 37
 
30 38
     public void setSlidingListener(SlidingListener listener) {
@@ -32,30 +40,60 @@ public class SlidingOverlay {
32 40
     }
33 41
 
34 42
     public void show() {
35
-        final ContentView view = createSlidingOverlayView(params);
36 43
         parent.addView(view);
37 44
 
38
-        final PeekingAnimator animator = new PeekingAnimator(view);
45
+        final PeekingAnimator animator = new PeekingAnimator(view, true);
39 46
         animator.addListener(new AnimatorListenerAdapter() {
40 47
             @Override
41 48
             public void onAnimationCancel(Animator animator) {
42
-                onSlidingOverlayEnd(view);
49
+                onSlidingOverlayShown(view);
43 50
             }
44 51
 
45 52
             @Override
46 53
             public void onAnimationEnd(Animator animator) {
47
-                onSlidingOverlayEnd(view);
54
+                onSlidingOverlayShown(view);
48 55
             }
49 56
         });
50 57
         view.setOnDisplayListener(new Screen.OnDisplayListener() {
51 58
             @Override
52 59
             public void onDisplay() {
53 60
                 view.setVisibility(View.VISIBLE);
61
+                visibilityState = VisibilityState.AnimateShow;
54 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 97
     protected ContentView createSlidingOverlayView(SlidingOverlayParams params) {
60 98
         final float heightPixels = ViewUtils.convertDpToPixel(100);
61 99
 
@@ -68,7 +106,15 @@ public class SlidingOverlay {
68 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 116
     protected void onSlidingOverlayEnd(ContentView view) {
117
+        visibilityState = VisibilityState.Hidden;
72 118
         view.unmountReactView();
73 119
         parent.removeView(view);
74 120
 

+ 58
- 0
android/app/src/main/java/com/reactnativenavigation/views/slidingOverlay/SlidingOverlaysQueue.java Wyświetl plik

@@ -4,9 +4,16 @@ import com.reactnativenavigation.NavigationApplication;
4 4
 
5 5
 import java.util.LinkedList;
6 6
 import java.util.Queue;
7
+import java.util.Timer;
8
+import java.util.TimerTask;
7 9
 
8 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 17
     protected Queue<SlidingOverlay> queue = new LinkedList<>();
11 18
 
12 19
     public void add(final SlidingOverlay slidingOverlay) {
@@ -17,10 +24,61 @@ public class SlidingOverlaysQueue implements SlidingOverlay.SlidingListener{
17 24
                 if (queue.size() == 1) {
18 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 82
     @Override
25 83
     public void onSlidingOverlayGone() {
26 84
         queue.poll();

+ 4
- 1
example/src/screens/InAppNotification.js Wyświetl plik

@@ -33,7 +33,10 @@ export default class InAppNotification extends Component {
33 33
     return (
34 34
       <View style={styleSheet.container}>
35 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 40
             <Text>Tap Me</Text>
38 41
           </TouchableHighlight>
39 42
       </View>

+ 5
- 0
src/deprecated/platformSpecificDeprecated.android.js Wyświetl plik

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

+ 5
- 0
src/platformSpecific.android.js Wyświetl plik

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