Browse Source

Initial implementation of willAppear and willDisappear (#2748)

* Initial implementation of willAppear and willDisappear

Currently only implemented on pop in StackController

* call clearOptions on super
* TopTabs visible

* fix unit
Guy Carmeli 7 years ago
parent
commit
bdca58996c
No account linked to committer's email address

+ 8
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabsOptions.java View File

@@ -3,6 +3,10 @@ package com.reactnativenavigation.parse;
3 3
 import android.support.annotation.NonNull;
4 4
 import android.support.annotation.Nullable;
5 5
 
6
+import com.reactnativenavigation.parse.params.Bool;
7
+import com.reactnativenavigation.parse.params.NullBool;
8
+import com.reactnativenavigation.parse.parsers.BoolParser;
9
+
6 10
 import org.json.JSONObject;
7 11
 
8 12
 public class TopTabsOptions implements DEFAULT_VALUES {
@@ -10,6 +14,7 @@ public class TopTabsOptions implements DEFAULT_VALUES {
10 14
     @NonNull public Color selectedTabColor = new NullColor();
11 15
     @NonNull public Color unselectedTabColor = new NullColor();
12 16
     @NonNull public Number fontSize = new NullNumber();
17
+    @NonNull public Bool visible = new NullBool();
13 18
 
14 19
     public static TopTabsOptions parse(@Nullable JSONObject json) {
15 20
         TopTabsOptions result = new TopTabsOptions();
@@ -17,6 +22,7 @@ public class TopTabsOptions implements DEFAULT_VALUES {
17 22
         result.selectedTabColor = ColorParser.parse(json, "selectedColor");
18 23
         result.unselectedTabColor = ColorParser.parse(json, "unselectedTabColor");
19 24
         result.fontSize = NumberParser.parse(json, "fontSize");
25
+        result.visible = BoolParser.parse(json, "visible");
20 26
         return result;
21 27
     }
22 28
 
@@ -24,11 +30,13 @@ public class TopTabsOptions implements DEFAULT_VALUES {
24 30
         if (other.selectedTabColor.hasValue()) selectedTabColor = other.selectedTabColor;
25 31
         if (other.unselectedTabColor.hasValue()) unselectedTabColor = other.unselectedTabColor;
26 32
         if (other.fontSize.hasValue()) fontSize = other.fontSize;
33
+        if (other.visible.hasValue()) visible = other.visible;
27 34
     }
28 35
 
29 36
     void mergeWithDefault(TopTabsOptions defaultOptions) {
30 37
         if (!selectedTabColor.hasValue()) selectedTabColor = defaultOptions.selectedTabColor;
31 38
         if (!unselectedTabColor.hasValue()) unselectedTabColor = defaultOptions.unselectedTabColor;
32 39
         if (!fontSize.hasValue()) fontSize = defaultOptions.fontSize;
40
+        if (!visible.hasValue()) visible = defaultOptions.visible;
33 41
     }
34 42
 }

+ 1
- 0
lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java View File

@@ -60,6 +60,7 @@ public class OptionsPresenter {
60 60
     private void applyTopTabsOptions(TopTabsOptions options) {
61 61
         topBar.applyTopTabsColors(options.selectedTabColor, options.unselectedTabColor);
62 62
         topBar.applyTopTabsFontSize(options.fontSize);
63
+        topBar.setTopTabsVisible(options.visible.isTrueOrUndefined());
63 64
     }
64 65
 
65 66
     private void applyTopTabOptions(TopTabOptions topTabOptions) {

+ 5
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java View File

@@ -75,10 +75,15 @@ public abstract class ParentController<T extends ViewGroup> extends ViewControll
75 75
 
76 76
 	@CallSuper
77 77
     void clearOptions() {
78
+	    applyOnParentController(parent -> ((ParentController) parent).clearOptions());
78 79
         options = initialOptions.copy();
79 80
     }
80 81
 
81 82
     public void setupTopTabsWithViewPager(ViewPager viewPager) {
82 83
 
83 84
     }
85
+
86
+    public void clearTopTabs() {
87
+
88
+    }
84 89
 }

+ 19
- 13
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java View File

@@ -88,13 +88,10 @@ public class StackController extends ParentController <StackLayout> {
88 88
         }
89 89
 
90 90
         final ViewController poppedTop = stack.pop();
91
-        ViewController newTop = stack.peek();
91
+        final ViewController newTop = stack.peek();
92
+        popInternal(poppedTop, newTop);
92 93
 
93
-        View enteringView = newTop.getView();
94
-        final View exitingView = poppedTop.getView();
95
-        getView().addView(enteringView, getView().getChildCount() - 1);
96
-
97
-        finishPopping(exitingView, poppedTop, promise);
94
+        finishPopping(poppedTop.getView(), poppedTop, promise);
98 95
     }
99 96
 
100 97
 	private void animatePop(final Promise promise) {
@@ -104,15 +101,19 @@ public class StackController extends ParentController <StackLayout> {
104 101
 		}
105 102
 
106 103
 		final ViewController poppedTop = stack.pop();
107
-		ViewController newTop = stack.peek();
104
+        final ViewController newTop = stack.peek();
105
+        popInternal(poppedTop, newTop);
108 106
 
109
-		View enteringView = newTop.getView();
110
-		final View exitingView = poppedTop.getView();
111
-		getView().addView(enteringView, getView().getChildCount() - 1);
112
-
113
-        animator.animatePop(exitingView, () -> finishPopping(exitingView, poppedTop, promise));
107
+        animator.animatePop(poppedTop.getView(), () -> finishPopping(poppedTop.getView(), poppedTop, promise));
114 108
 	}
115 109
 
110
+    private void popInternal(ViewController poppedTop, ViewController newTop) {
111
+        poppedTop.onViewWillDisappear();
112
+        newTop.onViewWillAppear();
113
+        View enteringView = newTop.getView();
114
+        getView().addView(enteringView, getView().getChildCount() - 1);
115
+    }
116
+
116 117
     boolean canPop() {
117 118
         return stack.size() > 1;
118 119
     }
@@ -200,6 +201,11 @@ public class StackController extends ParentController <StackLayout> {
200 201
 
201 202
     @Override
202 203
     public void setupTopTabsWithViewPager(ViewPager viewPager) {
203
-        stackLayout.setupTopTabsWithViewPager(viewPager);
204
+        stackLayout.initTopTabs(viewPager);
205
+    }
206
+
207
+    @Override
208
+    public void clearTopTabs() {
209
+        stackLayout.clearTopTabs();
204 210
     }
205 211
 }

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

@@ -133,6 +133,10 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
133 133
         return getView().equals(component);
134 134
     }
135 135
 
136
+    public void onViewWillAppear() {
137
+
138
+    }
139
+
136 140
     public void onViewAppeared() {
137 141
         isShown = true;
138 142
         applyOptions(options);
@@ -142,6 +146,10 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
142 146
         });
143 147
     }
144 148
 
149
+    public void onViewWillDisappear() {
150
+
151
+    }
152
+
145 153
     public void onViewDisappear() {
146 154
         isShown = false;
147 155
     }

+ 6
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java View File

@@ -57,6 +57,12 @@ public class TopTabsController extends ParentController<TopTabsViewPager> implem
57 57
         performOnCurrentTab(ViewController::onViewAppeared);
58 58
     }
59 59
 
60
+    @Override
61
+    public void onViewWillDisappear() {
62
+        super.onViewWillDisappear();
63
+        applyOnParentController(parentController -> ((ParentController) parentController).clearTopTabs());
64
+    }
65
+
60 66
     @Override
61 67
     public void onViewDisappear() {
62 68
         performOnCurrentTab(ViewController::onViewDisappear);

+ 6
- 2
lib/android/app/src/main/java/com/reactnativenavigation/views/StackLayout.java View File

@@ -46,7 +46,11 @@ public class StackLayout extends RelativeLayout implements TitleBarButton.OnClic
46 46
         topBar.clear();
47 47
     }
48 48
 
49
-    public void setupTopTabsWithViewPager(ViewPager viewPager) {
50
-        topBar.setupTopTabsWithViewPager(viewPager);
49
+    public void initTopTabs(ViewPager viewPager) {
50
+        topBar.initTopTabs(viewPager);
51
+    }
52
+
53
+    public void clearTopTabs() {
54
+        topBar.clearTopTabs();
51 55
     }
52 56
 }

+ 16
- 7
lib/android/app/src/main/java/com/reactnativenavigation/views/TopBar.java View File

@@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
4 4
 import android.content.Context;
5 5
 import android.graphics.Typeface;
6 6
 import android.support.annotation.Nullable;
7
+import android.support.annotation.VisibleForTesting;
7 8
 import android.support.design.widget.AppBarLayout;
8 9
 import android.support.v4.view.ViewPager;
9 10
 import android.support.v7.widget.Toolbar;
@@ -90,6 +91,10 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
90 91
         topTabs.applyTopTabsFontSize(fontSize);
91 92
     }
92 93
 
94
+    public void setTopTabsVisible(boolean visible) {
95
+        topTabs.setVisibility(this, visible);
96
+    }
97
+
93 98
     public void setButtons(ArrayList<Button> leftButtons, ArrayList<Button> rightButtons) {
94 99
         setLeftButtons(leftButtons);
95 100
         setRightButtons(rightButtons);
@@ -156,13 +161,9 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
156 161
         return titleBar;
157 162
     }
158 163
 
159
-    public void setupTopTabsWithViewPager(ViewPager viewPager) {
160
-        initTopTabs();
161
-        topTabs.setupWithViewPager(viewPager);
162
-    }
163
-
164
-    private void initTopTabs() {
164
+    public void initTopTabs(ViewPager viewPager) {
165 165
         topTabs = new TopTabs(getContext());
166
+        topTabs.init(viewPager);
166 167
         addView(topTabs);
167 168
     }
168 169
 
@@ -210,6 +211,14 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
210 211
         titleBar.setTitle(null);
211 212
         titleBar.setNavigationIcon(null);
212 213
         titleBar.getMenu().clear();
213
-        removeView(topTabs);
214
+    }
215
+
216
+    @VisibleForTesting()
217
+    public TopTabs getTopTabs() {
218
+        return topTabs;
219
+    }
220
+
221
+    public void clearTopTabs() {
222
+        topTabs.clear(this);
214 223
     }
215 224
 }

+ 22
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabs.java View File

@@ -3,6 +3,8 @@ package com.reactnativenavigation.views;
3 3
 import android.content.Context;
4 4
 import android.graphics.Typeface;
5 5
 import android.support.design.widget.TabLayout;
6
+import android.support.v4.view.ViewPager;
7
+import android.view.ViewManager;
6 8
 
7 9
 import com.reactnativenavigation.parse.Color;
8 10
 import com.reactnativenavigation.parse.Number;
@@ -34,4 +36,24 @@ public class TopTabs extends TabLayout {
34 36
     public void applyTopTabsFontSize(Number fontSize) {
35 37
         styleHelper.applyTopTabsFontSize(fontSize);
36 38
     }
39
+
40
+    public void setVisibility(TopBar topBar, boolean visible) {
41
+        if (visible && getTabCount() > 0) {
42
+            if (getParent() == null) {
43
+                topBar.addView(this, 1);
44
+            }
45
+            setVisibility(VISIBLE);
46
+        } else {
47
+            topBar.removeView(this);
48
+        }
49
+    }
50
+
51
+    public void clear(ViewManager parent) {
52
+        setupWithViewPager(null);
53
+        parent.removeView(this);
54
+    }
55
+
56
+    public void init(ViewPager viewPager) {
57
+        setupWithViewPager(viewPager);
58
+    }
37 59
 }

+ 4
- 5
lib/android/app/src/test/java/com/reactnativenavigation/parse/OptionsTest.java View File

@@ -26,7 +26,7 @@ public class OptionsTest extends BaseTest {
26 26
     private static final Bool TOP_BAR_DRAW_BEHIND = new Bool(true);
27 27
     private static final Bool TOP_BAR_HIDE_ON_SCROLL = new Bool(true);
28 28
     private static final Bool BOTTOM_TABS_ANIMATE_HIDE = new Bool(true);
29
-    private static final Bool BOTTOM_TABS_HIDDEN = new Bool(true);
29
+    private static final Bool BOTTOM_TABS_VISIBLE = new Bool(true);
30 30
     private static final String BOTTOM_TABS_BADGE = "3";
31 31
     private static final String BOTTOM_TABS_CURRENT_TAB_ID = "ComponentId";
32 32
     private static final int BOTTOM_TABS_CURRENT_TAB_INDEX = 1;
@@ -62,7 +62,7 @@ public class OptionsTest extends BaseTest {
62 62
         assertThat(result.topBarOptions.drawBehind.get()).isEqualTo(TOP_BAR_DRAW_BEHIND.get());
63 63
         assertThat(result.topBarOptions.hideOnScroll.get()).isEqualTo(TOP_BAR_HIDE_ON_SCROLL.get());
64 64
         assertThat(result.bottomTabsOptions.animateHide.get()).isEqualTo(BOTTOM_TABS_ANIMATE_HIDE.get());
65
-        assertThat(result.bottomTabsOptions.visible.get()).isEqualTo(BOTTOM_TABS_HIDDEN.get());
65
+        assertThat(result.bottomTabsOptions.visible.get()).isEqualTo(BOTTOM_TABS_VISIBLE.get());
66 66
         assertThat(result.bottomTabsOptions.currentTabId.get()).isEqualTo(BOTTOM_TABS_CURRENT_TAB_ID);
67 67
         assertThat(result.bottomTabsOptions.currentTabIndex).isEqualTo(BOTTOM_TABS_CURRENT_TAB_INDEX);
68 68
     }
@@ -72,7 +72,7 @@ public class OptionsTest extends BaseTest {
72 72
         return new JSONObject()
73 73
                 .put("currentTabId", BOTTOM_TABS_CURRENT_TAB_ID)
74 74
                 .put("currentTabIndex", BOTTOM_TABS_CURRENT_TAB_INDEX)
75
-                .put("visible", BOTTOM_TABS_HIDDEN.get())
75
+                .put("visible", BOTTOM_TABS_VISIBLE.get())
76 76
                 .put("animateHide", BOTTOM_TABS_ANIMATE_HIDE.get());
77 77
     }
78 78
 
@@ -105,7 +105,7 @@ public class OptionsTest extends BaseTest {
105 105
         return new JSONObject()
106 106
                 .put("currentTabId", BOTTOM_TABS_CURRENT_TAB_ID)
107 107
                 .put("currentTabIndex", BOTTOM_TABS_CURRENT_TAB_INDEX)
108
-                .put("visible", BOTTOM_TABS_HIDDEN)
108
+                .put("visible", BOTTOM_TABS_VISIBLE)
109 109
                 .put("animateHide", BOTTOM_TABS_ANIMATE_HIDE)
110 110
                 .put("tabBadge", BOTTOM_TABS_BADGE);
111 111
     }
@@ -191,7 +191,6 @@ public class OptionsTest extends BaseTest {
191 191
         assertThat(uut.topTabsOptions.fontSize.hasValue()).isFalse();
192 192
     }
193 193
 
194
-
195 194
     @Test
196 195
     public void clear_topTabOptions() throws Exception {
197 196
         Options uut = new Options();

+ 9
- 0
lib/android/app/src/test/java/com/reactnativenavigation/utils/ViewHelper.java View File

@@ -0,0 +1,9 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import android.view.View;
4
+
5
+public class ViewHelper {
6
+    public static boolean isVisible(View view) {
7
+        return view != null && view.getParent() != null && view.getVisibility() == View.VISIBLE;
8
+    }
9
+}

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

@@ -138,7 +138,7 @@ public class OptionsApplyingTest extends BaseTest {
138 138
     }
139 139
 
140 140
     @Test
141
-    public void appliesTopBarHidden() throws Exception {
141
+    public void appliesTopBarVisible() throws Exception {
142 142
         assertThat(uut.initialOptions).isSameAs(initialNavigationOptions);
143 143
         initialNavigationOptions.topBarOptions.title = new Text("the title");
144 144
         uut.ensureViewIsCreated();

+ 21
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackControllerTest.java View File

@@ -8,6 +8,7 @@ import com.reactnativenavigation.mocks.MockPromise;
8 8
 import com.reactnativenavigation.mocks.SimpleViewController;
9 9
 import com.reactnativenavigation.parse.Options;
10 10
 import com.reactnativenavigation.parse.Text;
11
+import com.reactnativenavigation.utils.ViewHelper;
11 12
 import com.reactnativenavigation.views.ReactComponent;
12 13
 
13 14
 import org.assertj.core.api.iterable.Extractor;
@@ -294,6 +295,17 @@ public class StackControllerTest extends BaseTest {
294 295
         });
295 296
     }
296 297
 
298
+    @Test
299
+    public void pop_callWillAppearWillDisappear() throws Exception {
300
+        child1 = spy(child1);
301
+        child2 = spy(child2);
302
+        uut.push(child1, new MockPromise());
303
+        uut.push(child2, new MockPromise());
304
+        uut.pop(new MockPromise());
305
+        verify(child1, times(1)).onViewWillAppear();
306
+        verify(child2, times(1)).onViewWillDisappear();
307
+    }
308
+
297 309
     @Test
298 310
     public void popSpecific_CallsDestroyOnPoppedChild() throws Exception {
299 311
         child1 = spy(child1);
@@ -360,6 +372,15 @@ public class StackControllerTest extends BaseTest {
360 372
         assertThat(optionsCaptor.getValue().topBarOptions.title.hasValue()).isFalse();
361 373
     }
362 374
 
375
+    @Test
376
+    public void applyOptions_topTabsAreNotVisibleIfNoTabsAreDefined() throws Exception {
377
+        uut.ensureViewIsCreated();
378
+        uut.push(child1, new MockPromise());
379
+        child1.ensureViewIsCreated();
380
+        child1.onViewAppeared();
381
+        assertThat(ViewHelper.isVisible(uut.getTopBar().getTopTabs())).isFalse();
382
+    }
383
+
363 384
     private void assertContainsOnlyId(String... ids) {
364 385
         assertThat(uut.size()).isEqualTo(ids.length);
365 386
         assertThat(uut.getChildControllers()).extracting((Extractor<ViewController, String>) ViewController::getId).containsOnly(ids);

+ 25
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java View File

@@ -10,6 +10,7 @@ import com.reactnativenavigation.mocks.TestComponentViewCreator;
10 10
 import com.reactnativenavigation.mocks.TestReactView;
11 11
 import com.reactnativenavigation.parse.Options;
12 12
 import com.reactnativenavigation.parse.Text;
13
+import com.reactnativenavigation.utils.ViewHelper;
13 14
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsAdapter;
14 15
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
15 16
 import com.reactnativenavigation.views.ReactComponent;
@@ -207,6 +208,30 @@ public class TopTabsViewControllerTest extends BaseTest {
207 208
         assertThat(optionsCaptor.getValue().topTabOptions.title.hasValue()).isFalse();
208 209
     }
209 210
 
211
+    @Test
212
+    public void applyOptions_tabsAreRemovedBeforeViewDisappears() throws Exception {
213
+        parentController.getView().removeAllViews();
214
+
215
+        StackController stackController = spy(new StackController(activity, "stack", new Options()));
216
+        ComponentViewController first = new ComponentViewController(
217
+                activity,
218
+                "firstScreen",
219
+                "comp1",
220
+                new TestComponentViewCreator(),
221
+                new Options()
222
+        );
223
+        stackController.push(first, new MockPromise());
224
+        stackController.push(uut, new MockPromise());
225
+
226
+        first.ensureViewIsCreated();
227
+        uut.ensureViewIsCreated();
228
+        uut.onViewAppeared();
229
+
230
+        assertThat(ViewHelper.isVisible(stackController.getTopBar().getTopTabs())).isTrue();
231
+        stackController.pop(new MockPromise());
232
+        assertThat(ViewHelper.isVisible(stackController.getTopBar().getTopTabs())).isFalse();
233
+    }
234
+
210 235
     private IReactView tab(TopTabsViewPager topTabs, final int index) {
211 236
         return (IReactView) ((ViewGroup) topTabs.getChildAt(index)).getChildAt(0);
212 237
     }