Преглед на файлове

Add waitForRender options for setRoot animation and TopBar components

When set, the ui will become visible only after the root screen and TopBar components have been rendered.

Usage
Add the following to the root layout’s options, or to its child
```
animations: {
  setRoot: {
    waitForRender: true
  }
}
```

TopBar components (Title, background, buttons) can be synced as well by adding `waitForRender: true`, for example:
```
component: {
  name: 'navigation.playground.CustomTopBar',
  alignment: 'center',
  waitForRender: true,
}
```
Guy Carmeli преди 6 години
родител
ревизия
c746621c40
променени са 34 файла, в които са добавени 419 реда и са изтрити 243 реда
  1. 7
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Component.java
  2. 10
    5
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java
  3. 7
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarBackgroundOptions.java
  4. 0
    1
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/Presenter.java
  5. 13
    0
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/RenderChecker.java
  6. 95
    41
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackPresenter.java
  7. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java
  8. 29
    2
      lib/android/app/src/main/java/com/reactnativenavigation/utils/CollectionUtils.java
  9. 4
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/Functions.java
  10. 11
    4
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ObjectUtils.java
  11. 7
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarButtonController.java
  12. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarReactViewController.java
  13. 3
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java
  14. 10
    5
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java
  15. 15
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenter.java
  16. 12
    14
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java
  17. 1
    16
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java
  18. 2
    7
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarBackgroundViewController.java
  19. 4
    4
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarController.java
  20. 1
    3
      lib/android/app/src/main/java/com/reactnativenavigation/views/Component.java
  21. 5
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/Renderable.java
  22. 4
    5
      lib/android/app/src/main/java/com/reactnativenavigation/views/StackLayout.java
  23. 9
    13
      lib/android/app/src/main/java/com/reactnativenavigation/views/topbar/TopBar.java
  24. 4
    6
      lib/android/app/src/test/java/com/reactnativenavigation/TestUtils.java
  25. 58
    0
      lib/android/app/src/test/java/com/reactnativenavigation/presentation/RenderCheckerTest.java
  26. 4
    6
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java
  27. 24
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackPresenterTest.java
  28. 2
    5
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopBarButtonControllerTest.java
  29. 3
    5
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopBarControllerTest.java
  30. 5
    3
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/NavigatorTest.java
  31. 30
    0
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenterTest.java
  32. 34
    13
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.java
  33. 0
    70
      lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarBackgroundComponentTest.java
  34. 2
    5
      lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarTest.java

+ 7
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/Component.java Целия файл

@@ -1,7 +1,10 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3
+import com.reactnativenavigation.parse.params.Bool;
4
+import com.reactnativenavigation.parse.params.NullBool;
3 5
 import com.reactnativenavigation.parse.params.NullText;
4 6
 import com.reactnativenavigation.parse.params.Text;
7
+import com.reactnativenavigation.parse.parsers.BoolParser;
5 8
 import com.reactnativenavigation.parse.parsers.TextParser;
6 9
 
7 10
 import org.json.JSONObject;
@@ -14,6 +17,7 @@ public class Component {
14 17
         result.name = TextParser.parse(json, "name");
15 18
         result.componentId = TextParser.parse(json, "componentId");
16 19
         result.alignment = Alignment.fromString(TextParser.parse(json, "alignment").get(""));
20
+        result.waitForRender = BoolParser.parse(json, "waitForRender");
17 21
 
18 22
         return result;
19 23
     }
@@ -21,16 +25,19 @@ public class Component {
21 25
     public Text name = new NullText();
22 26
     public Text componentId = new NullText();
23 27
     public Alignment alignment = Alignment.Default;
28
+    public Bool waitForRender = new NullBool();
24 29
 
25 30
     void mergeWith(Component other) {
26 31
         if (other.componentId.hasValue()) componentId = other.componentId;
27 32
         if (other.name.hasValue()) name = other.name;
33
+        if (other.waitForRender.hasValue()) waitForRender = other.waitForRender;
28 34
         if (other.alignment != Alignment.Default) alignment = other.alignment;
29 35
     }
30 36
 
31 37
     public void mergeWithDefault(Component defaultOptions) {
32 38
         if (!componentId.hasValue()) componentId = defaultOptions.componentId;
33 39
         if (!name.hasValue()) name = defaultOptions.name;
40
+        if (!waitForRender.hasValue()) waitForRender = defaultOptions.waitForRender;
34 41
         if (alignment == Alignment.Default) alignment = defaultOptions.alignment;
35 42
     }
36 43
 

+ 10
- 5
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java Целия файл

@@ -7,6 +7,7 @@ import com.reactnativenavigation.presentation.BottomTabPresenter;
7 7
 import com.reactnativenavigation.presentation.BottomTabsPresenter;
8 8
 import com.reactnativenavigation.presentation.ComponentPresenter;
9 9
 import com.reactnativenavigation.presentation.Presenter;
10
+import com.reactnativenavigation.presentation.RenderChecker;
10 11
 import com.reactnativenavigation.presentation.SideMenuPresenter;
11 12
 import com.reactnativenavigation.presentation.StackPresenter;
12 13
 import com.reactnativenavigation.react.EventEmitter;
@@ -14,13 +15,12 @@ import com.reactnativenavigation.utils.ImageLoader;
14 15
 import com.reactnativenavigation.utils.TypefaceLoader;
15 16
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
16 17
 import com.reactnativenavigation.viewcontrollers.ComponentViewController;
17
-import com.reactnativenavigation.viewcontrollers.sidemenu.SideMenuController;
18 18
 import com.reactnativenavigation.viewcontrollers.ViewController;
19 19
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabsController;
20 20
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentCreator;
21 21
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentViewController;
22
+import com.reactnativenavigation.viewcontrollers.sidemenu.SideMenuController;
22 23
 import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
23
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
24 24
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
25 25
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
26 26
 import com.reactnativenavigation.views.ComponentViewCreator;
@@ -165,12 +165,17 @@ public class LayoutFactory {
165 165
         return new StackControllerBuilder(activity)
166 166
                 .setChildren(createChildren(node.children))
167 167
                 .setChildRegistry(childRegistry)
168
-                .setTopBarButtonCreator(new TitleBarButtonCreator(reactInstanceManager))
169
-                .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreator(reactInstanceManager)))
170 168
                 .setTopBarController(new TopBarController())
171 169
                 .setId(node.id)
172 170
                 .setInitialOptions(parse(typefaceManager, node.getOptions()))
173
-                .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreator(reactInstanceManager), new TitleBarButtonCreator(reactInstanceManager), new ImageLoader(), defaultOptions))
171
+                .setStackPresenter(new StackPresenter(activity,
172
+                        new TitleBarReactViewCreator(reactInstanceManager),
173
+                        new TopBarBackgroundViewCreator(reactInstanceManager),
174
+                        new TitleBarButtonCreator(reactInstanceManager),
175
+                        new ImageLoader(),
176
+                        new RenderChecker(),
177
+                        defaultOptions
178
+                ))
174 179
                 .setPresenter(new Presenter(activity, defaultOptions))
175 180
                 .build();
176 181
 	}

+ 7
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarBackgroundOptions.java Целия файл

@@ -2,8 +2,11 @@ package com.reactnativenavigation.parse;
2 2
 
3 3
 import android.graphics.Color;
4 4
 
5
+import com.reactnativenavigation.parse.params.Bool;
5 6
 import com.reactnativenavigation.parse.params.Colour;
7
+import com.reactnativenavigation.parse.params.NullBool;
6 8
 import com.reactnativenavigation.parse.params.NullColor;
9
+import com.reactnativenavigation.parse.parsers.BoolParser;
7 10
 import com.reactnativenavigation.parse.parsers.ColorParser;
8 11
 
9 12
 import org.json.JSONObject;
@@ -15,6 +18,7 @@ public class TopBarBackgroundOptions {
15 18
 
16 19
         options.color = ColorParser.parse(json, "color");
17 20
         options.component = Component.parse(json.optJSONObject("component"));
21
+        options.waitForRender = BoolParser.parse(json, "waitForRender");
18 22
 
19 23
         if (options.component.hasValue()) {
20 24
             options.color = new Colour(Color.TRANSPARENT);
@@ -25,14 +29,17 @@ public class TopBarBackgroundOptions {
25 29
 
26 30
     public Colour color = new NullColor();
27 31
     public Component component = new Component();
32
+    public Bool waitForRender = new NullBool();
28 33
 
29 34
     void mergeWith(final TopBarBackgroundOptions other) {
30 35
         if (other.color.hasValue()) color = other.color;
36
+        if (other.waitForRender.hasValue()) waitForRender = other.waitForRender;
31 37
         component.mergeWith(other.component);
32 38
     }
33 39
 
34 40
     void mergeWithDefault(TopBarBackgroundOptions defaultOptions) {
35 41
         if (!color.hasValue()) color = defaultOptions.color;
42
+        if (!waitForRender.hasValue()) waitForRender = defaultOptions.waitForRender;
36 43
         component.mergeWithDefault(defaultOptions.component);
37 44
     }
38 45
 }

+ 0
- 1
lib/android/app/src/main/java/com/reactnativenavigation/presentation/Presenter.java Целия файл

@@ -154,5 +154,4 @@ public class Presenter {
154 154
             }
155 155
         }
156 156
     }
157
-
158 157
 }

+ 13
- 0
lib/android/app/src/main/java/com/reactnativenavigation/presentation/RenderChecker.java Целия файл

@@ -0,0 +1,13 @@
1
+package com.reactnativenavigation.presentation;
2
+
3
+import com.reactnativenavigation.viewcontrollers.ViewController;
4
+
5
+import java.util.Collection;
6
+
7
+import static com.reactnativenavigation.utils.CollectionUtils.reduce;
8
+
9
+public class RenderChecker {
10
+    public boolean areRendered(Collection<ViewController> components) {
11
+        return reduce(components, true, ViewController::isRendered);
12
+    }
13
+}

+ 95
- 41
lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackPresenter.java Целия файл

@@ -9,6 +9,8 @@ import android.view.Gravity;
9 9
 import android.view.View;
10 10
 import android.view.ViewGroup.LayoutParams;
11 11
 import android.view.ViewGroup.MarginLayoutParams;
12
+import android.widget.FrameLayout;
13
+import android.widget.RelativeLayout;
12 14
 
13 15
 import com.reactnativenavigation.parse.Alignment;
14 16
 import com.reactnativenavigation.parse.AnimationsOptions;
@@ -22,15 +24,19 @@ import com.reactnativenavigation.parse.params.Button;
22 24
 import com.reactnativenavigation.parse.params.Colour;
23 25
 import com.reactnativenavigation.utils.ButtonPresenter;
24 26
 import com.reactnativenavigation.utils.ImageLoader;
27
+import com.reactnativenavigation.utils.ObjectUtils;
25 28
 import com.reactnativenavigation.utils.UiUtils;
26 29
 import com.reactnativenavigation.viewcontrollers.IReactView;
27 30
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
28 31
 import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
29 32
 import com.reactnativenavigation.viewcontrollers.TitleBarReactViewController;
33
+import com.reactnativenavigation.viewcontrollers.ViewController;
30 34
 import com.reactnativenavigation.viewcontrollers.button.NavigationIconResolver;
35
+import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
31 36
 import com.reactnativenavigation.views.Component;
32 37
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
33 38
 import com.reactnativenavigation.views.topbar.TopBar;
39
+import com.reactnativenavigation.views.topbar.TopBarBackgroundViewCreator;
34 40
 
35 41
 import java.util.ArrayList;
36 42
 import java.util.Collections;
@@ -39,9 +45,11 @@ import java.util.LinkedHashMap;
39 45
 import java.util.List;
40 46
 import java.util.Map;
41 47
 
48
+import static com.reactnativenavigation.utils.CollectionUtils.filter;
42 49
 import static com.reactnativenavigation.utils.CollectionUtils.forEach;
43 50
 import static com.reactnativenavigation.utils.CollectionUtils.keyBy;
44 51
 import static com.reactnativenavigation.utils.CollectionUtils.merge;
52
+import static com.reactnativenavigation.utils.ObjectUtils.perform;
45 53
 
46 54
 public class StackPresenter {
47 55
     private static final int DEFAULT_TITLE_COLOR = Color.BLACK;
@@ -56,17 +64,28 @@ public class StackPresenter {
56 64
     private final TitleBarReactViewCreator titleViewCreator;
57 65
     private TitleBarButtonController.OnClickListener onClickListener;
58 66
     private final ImageLoader imageLoader;
67
+    private final RenderChecker renderChecker;
68
+    private final TopBarBackgroundViewCreator topBarBackgroundViewCreator;
59 69
     private final ReactViewCreator buttonCreator;
60 70
     private Options defaultOptions;
61
-    private Map<Component, TitleBarReactViewController> titleComponentViewControllers = new HashMap<>();
62
-    private Map<Component, Map<String, TitleBarButtonController>> componentRightButtons = new HashMap<>();
63
-    private Map<Component, Map<String, TitleBarButtonController>> componentLeftButtons = new HashMap<>();
64
-
65
-    public StackPresenter(Activity activity, TitleBarReactViewCreator titleViewCreator, ReactViewCreator buttonCreator, ImageLoader imageLoader, Options defaultOptions) {
71
+    private Map<Component, TitleBarReactViewController> titleControllers = new HashMap();
72
+    private Map<Component, TopBarBackgroundViewController> backgroundControllers = new HashMap();
73
+    private Map<Component, Map<String, TitleBarButtonController>> componentRightButtons = new HashMap();
74
+    private Map<Component, Map<String, TitleBarButtonController>> componentLeftButtons = new HashMap();
75
+
76
+    public StackPresenter(Activity activity,
77
+                          TitleBarReactViewCreator titleViewCreator,
78
+                          TopBarBackgroundViewCreator topBarBackgroundViewCreator,
79
+                          ReactViewCreator buttonCreator,
80
+                          ImageLoader imageLoader,
81
+                          RenderChecker renderChecker,
82
+                          Options defaultOptions) {
66 83
         this.activity = activity;
67 84
         this.titleViewCreator = titleViewCreator;
85
+        this.topBarBackgroundViewCreator = topBarBackgroundViewCreator;
68 86
         this.buttonCreator = buttonCreator;
69 87
         this.imageLoader = imageLoader;
88
+        this.renderChecker = renderChecker;
70 89
         this.defaultOptions = defaultOptions;
71 90
         defaultTitleFontSize = UiUtils.dpToSp(activity, 18);
72 91
         defaultSubtitleFontSize = UiUtils.dpToSp(activity, 14);
@@ -84,26 +103,17 @@ public class StackPresenter {
84 103
         return defaultOptions;
85 104
     }
86 105
 
87
-    public List<TitleBarButtonController> getComponentButtons(Component child) {
88
-        return merge(getRightButtons(child), getLeftButtons(child), Collections.EMPTY_LIST);
89
-    }
90
-
91
-    public List<TitleBarButtonController> getComponentButtons(Component child, List<TitleBarButtonController> defaultValue) {
92
-        return merge(getRightButtons(child), getLeftButtons(child), defaultValue);
93
-    }
94
-
95
-    private List<TitleBarButtonController> getRightButtons(Component child) {
96
-        return componentRightButtons.containsKey(child) ? new ArrayList<>(componentRightButtons.get(child).values()) : null;
97
-    }
98
-
99
-    private List<TitleBarButtonController> getLeftButtons(Component child) {
100
-        return componentLeftButtons.containsKey(child) ? new ArrayList<>(componentLeftButtons.get(child).values()) : null;
101
-    }
102
-
103 106
     public void bindView(TopBar topBar) {
104 107
         this.topBar = topBar;
105 108
     }
106 109
 
110
+    public boolean isRendered(Component component) {
111
+        ArrayList<ViewController> controllers = new ArrayList<>(perform(componentRightButtons.get(component), new ArrayList<>(), Map::values));
112
+        controllers.add(backgroundControllers.get(component));
113
+        controllers.add(titleControllers.get(component));
114
+        return renderChecker.areRendered(filter(controllers, ObjectUtils::notNull));
115
+    }
116
+
107 117
     public void applyLayoutParamsOptions(Options options, View view) {
108 118
         Options withDefault = options.copy().withDefaultOptions(defaultOptions);
109 119
         if (view instanceof Component) {
@@ -143,22 +153,16 @@ public class StackPresenter {
143 153
     }
144 154
 
145 155
     public void onChildDestroyed(Component child) {
146
-        TitleBarReactViewController removed = titleComponentViewControllers.remove(child);
147
-        if (removed != null) {
148
-            removed.destroy();
149
-        }
156
+        perform(titleControllers.remove(child), TitleBarReactViewController::destroy);
157
+        perform(backgroundControllers.remove(child), TopBarBackgroundViewController::destroy);
150 158
         destroyButtons(componentRightButtons.get(child));
151 159
         destroyButtons(componentLeftButtons.get(child));
152 160
         componentRightButtons.remove(child);
153 161
         componentLeftButtons.remove(child);
154 162
     }
155 163
 
156
-    private void destroyButtons(Map<String, TitleBarButtonController> buttons) {
157
-        if (buttons != null) {
158
-            for (TitleBarButtonController button : buttons.values()) {
159
-                button.destroy();
160
-            }
161
-        }
164
+    private void destroyButtons(@Nullable Map<String, TitleBarButtonController> buttons) {
165
+        if (buttons != null) forEach(buttons.values(), ViewController::destroy);
162 166
     }
163 167
 
164 168
     private void applyTopBarOptions(TopBarOptions options, AnimationsOptions animationOptions, Component component, Options componentOptions) {
@@ -172,11 +176,12 @@ public class StackPresenter {
172 176
         topBar.setTitle(options.title.text.get(""));
173 177
 
174 178
         if (options.title.component.hasValue()) {
175
-            if (titleComponentViewControllers.containsKey(component)) {
176
-                topBar.setTitleComponent(titleComponentViewControllers.get(component).getView());
179
+            if (titleControllers.containsKey(component)) {
180
+                topBar.setTitleComponent(titleControllers.get(component).getView());
177 181
             } else {
178 182
                 TitleBarReactViewController controller = new TitleBarReactViewController(activity, titleViewCreator);
179
-                titleComponentViewControllers.put(component, controller);
183
+                controller.setWaitForRender(options.title.component.waitForRender);
184
+                titleControllers.put(component, controller);
180 185
                 controller.setComponent(options.title.component);
181 186
                 controller.getView().setLayoutParams(getComponentLayoutParams(options.title.component));
182 187
                 topBar.setTitleComponent(controller.getView());
@@ -198,7 +203,20 @@ public class StackPresenter {
198 203
         topBar.setBorderColor(options.borderColor.get(DEFAULT_BORDER_COLOR));
199 204
 
200 205
         topBar.setBackgroundColor(options.background.color.get(Color.WHITE));
201
-        topBar.setBackgroundComponent(options.background.component);
206
+
207
+        if (options.background.component.hasValue()) {
208
+            if (backgroundControllers.containsKey(component)) {
209
+                topBar.setBackgroundComponent(backgroundControllers.get(component).getView());
210
+            } else {
211
+                TopBarBackgroundViewController controller = new TopBarBackgroundViewController(activity, topBarBackgroundViewCreator);
212
+                controller.setWaitForRender(options.background.waitForRender);
213
+                backgroundControllers.put(component, controller);
214
+                controller.setComponent(options.background.component);
215
+                controller.getView().setLayoutParams(new RelativeLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
216
+                topBar.setBackgroundComponent(controller.getView());
217
+            }
218
+        }
219
+
202 220
         if (options.testId.hasValue()) topBar.setTestId(options.testId.get());
203 221
         applyTopBarVisibility(options, animationOptions, componentOptions);
204 222
         if (options.drawBehind.isTrue() && !componentOptions.layout.topMargin.hasValue()) {
@@ -278,7 +296,7 @@ public class StackPresenter {
278 296
     }
279 297
 
280 298
     private TitleBarButtonController createButtonController(Button button) {
281
-        return new TitleBarButtonController(activity,
299
+        TitleBarButtonController controller = new TitleBarButtonController(activity,
282 300
                 new NavigationIconResolver(activity, imageLoader),
283 301
                 imageLoader,
284 302
                 new ButtonPresenter(topBar.getTitleBar(), button),
@@ -286,6 +304,8 @@ public class StackPresenter {
286 304
                 buttonCreator,
287 305
                 onClickListener
288 306
         );
307
+        controller.setWaitForRender(button.component.waitForRender);
308
+        return controller;
289 309
     }
290 310
 
291 311
     private void applyTopTabsOptions(TopTabsOptions options) {
@@ -371,11 +391,11 @@ public class StackPresenter {
371 391
         if (options.title.text.hasValue()) topBar.setTitle(options.title.text.get());
372 392
 
373 393
         if (options.title.component.hasValue()) {
374
-            if (titleComponentViewControllers.containsKey(component)) {
375
-                topBar.setTitleComponent(titleComponentViewControllers.get(component).getView());
394
+            if (titleControllers.containsKey(component)) {
395
+                topBar.setTitleComponent(titleControllers.get(component).getView());
376 396
             } else {
377 397
                 TitleBarReactViewController controller = new TitleBarReactViewController(activity, titleViewCreator);
378
-                titleComponentViewControllers.put(component, controller);
398
+                titleControllers.put(component, controller);
379 399
                 controller.setComponent(options.title.component);
380 400
                 controller.getView().setLayoutParams(getComponentLayoutParams(options.title.component));
381 401
                 topBar.setTitleComponent(controller.getView());
@@ -392,7 +412,18 @@ public class StackPresenter {
392 412
         if (options.subtitle.fontFamily != null) topBar.setSubtitleFontFamily(options.subtitle.fontFamily);
393 413
 
394 414
         if (options.background.color.hasValue()) topBar.setBackgroundColor(options.background.color.get());
395
-        if (options.background.component.hasValue()) topBar.setBackgroundComponent(options.background.component);
415
+
416
+        if (options.background.component.hasValue()) {
417
+            if (backgroundControllers.containsKey(component)) {
418
+                topBar.setBackgroundComponent(backgroundControllers.get(component).getView());
419
+            } else {
420
+                TopBarBackgroundViewController controller = new TopBarBackgroundViewController(activity, topBarBackgroundViewCreator);
421
+                backgroundControllers.put(component, controller);
422
+                controller.setComponent(options.background.component);
423
+                controller.getView().setLayoutParams(new RelativeLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
424
+                topBar.setBackgroundComponent(controller.getView());
425
+            }
426
+        }
396 427
 
397 428
         if (options.testId.hasValue()) topBar.setTestId(options.testId.get());
398 429
 
@@ -441,6 +472,29 @@ public class StackPresenter {
441 472
 
442 473
     @RestrictTo(RestrictTo.Scope.TESTS)
443 474
     public Map<Component, TitleBarReactViewController> getTitleComponents() {
444
-        return titleComponentViewControllers;
475
+        return titleControllers;
476
+    }
477
+
478
+    @RestrictTo(RestrictTo.Scope.TESTS)
479
+    public Map<Component, TopBarBackgroundViewController> getBackgroundComponents() {
480
+        return backgroundControllers;
481
+    }
482
+
483
+    @RestrictTo(RestrictTo.Scope.TESTS)
484
+    public List<TitleBarButtonController> getComponentButtons(Component child) {
485
+        return merge(getRightButtons(child), getLeftButtons(child), Collections.EMPTY_LIST);
486
+    }
487
+
488
+    @RestrictTo(RestrictTo.Scope.TESTS)
489
+    public List<TitleBarButtonController> getComponentButtons(Component child, List<TitleBarButtonController> defaultValue) {
490
+        return merge(getRightButtons(child), getLeftButtons(child), defaultValue);
491
+    }
492
+
493
+    private List<TitleBarButtonController> getRightButtons(Component child) {
494
+        return componentRightButtons.containsKey(child) ? new ArrayList<>(componentRightButtons.get(child).values()) : null;
495
+    }
496
+
497
+    private List<TitleBarButtonController> getLeftButtons(Component child) {
498
+        return componentLeftButtons.containsKey(child) ? new ArrayList<>(componentLeftButtons.get(child).values()) : null;
445 499
     }
446 500
 }

+ 2
- 1
lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java Целия файл

@@ -14,13 +14,14 @@ import com.facebook.react.uimanager.UIManagerModule;
14 14
 import com.facebook.react.uimanager.events.EventDispatcher;
15 15
 import com.reactnativenavigation.interfaces.ScrollEventListener;
16 16
 import com.reactnativenavigation.viewcontrollers.IReactView;
17
+import com.reactnativenavigation.views.Renderable;
17 18
 import com.reactnativenavigation.views.element.Element;
18 19
 
19 20
 import java.util.ArrayList;
20 21
 import java.util.List;
21 22
 
22 23
 @SuppressLint("ViewConstructor")
23
-public class ReactView extends ReactRootView implements IReactView {
24
+public class ReactView extends ReactRootView implements IReactView, Renderable {
24 25
 
25 26
 	private final ReactInstanceManager reactInstanceManager;
26 27
 	private final String componentId;

+ 29
- 2
lib/android/app/src/main/java/com/reactnativenavigation/utils/CollectionUtils.java Целия файл

@@ -63,7 +63,7 @@ public class CollectionUtils {
63 63
 
64 64
     public static <T> List<T> merge(@Nullable Collection<T> a, @Nullable Collection<T> b) {
65 65
         if (a == null && b == null) return null;
66
-        List<T> result = new ArrayList<>(get(a));
66
+        List<T> result = new ArrayList(get(a));
67 67
         result.addAll(get(b));
68 68
         return result;
69 69
     }
@@ -91,7 +91,34 @@ public class CollectionUtils {
91 91
         return items.remove(items.size() - 1);
92 92
     }
93 93
 
94
-    private static @NonNull <T> Collection<T> get(@Nullable Collection<T> t) {
94
+    public interface Reducer<S, T> {
95
+        S reduce(T item, S currentValue);
96
+    }
97
+
98
+    public static <S, T> S reduce(Collection<T> items, S initialValue, Reducer<S, T> reducer) {
99
+        S currentValue = initialValue;
100
+        for (T item : items) {
101
+            currentValue = reducer.reduce(item, currentValue);
102
+        }
103
+        return currentValue;
104
+    }
105
+
106
+    public static <T> Boolean reduce(@Nullable Collection<T> items, boolean initialValue, Functions.FuncR1<T, Boolean> reducer) {
107
+        boolean currentValue = initialValue;
108
+        if (CollectionUtils.isNullOrEmpty(items)) return currentValue;
109
+        for (T item : items) {
110
+            currentValue &= reducer.run(item);
111
+            if (!currentValue) return false;
112
+        }
113
+        return currentValue;
114
+    }
115
+
116
+    public static @NonNull <T> Collection<T> get(@Nullable Collection<T> t) {
95 117
         return t == null ? Collections.EMPTY_LIST : t;
96 118
     }
119
+
120
+    public static @NonNull <T> Collection<T> get(@Nullable Map<?, T> t) {
121
+        return t == null ? Collections.EMPTY_LIST : t.values();
122
+    }
123
+
97 124
 }

+ 4
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/Functions.java Целия файл

@@ -12,4 +12,8 @@ public class Functions {
12 12
     public interface Func1<T> {
13 13
         void run(T param);
14 14
     }
15
+
16
+    public interface FuncR1<T, S> {
17
+        S run(T param);
18
+    }
15 19
 }

+ 11
- 4
lib/android/app/src/main/java/com/reactnativenavigation/utils/ObjectUtils.java Целия файл

@@ -2,12 +2,19 @@ package com.reactnativenavigation.utils;
2 2
 
3 3
 import android.support.annotation.Nullable;
4 4
 
5
+import com.reactnativenavigation.utils.Functions.Func1;
6
+import com.reactnativenavigation.utils.Functions.FuncR1;
7
+
5 8
 public class ObjectUtils {
6
-    public interface Action<T> {
7
-        void performOn(T obj);
9
+    public static <T> void perform(@Nullable T obj, Func1<T> action) {
10
+        if (obj != null) action.run(obj);
11
+    }
12
+
13
+    public static <T, S> S perform(@Nullable T obj, S defaultValue, FuncR1<T, S> action) {
14
+        return obj == null ? defaultValue : action.run(obj);
8 15
     }
9 16
 
10
-    public static <T> void perform(@Nullable T obj, Action<T> action) {
11
-        if (obj != null) action.performOn(obj);
17
+    public static boolean notNull(Object o) {
18
+        return o != null;
12 19
     }
13 20
 }

+ 7
- 2
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarButtonController.java Целия файл

@@ -69,13 +69,18 @@ public class TitleBarButtonController extends ViewController<TitleBarReactButton
69 69
     @SuppressLint("MissingSuperCall")
70 70
     @Override
71 71
     public void onViewAppeared() {
72
-        view.sendComponentStart();
72
+        getView().sendComponentStart();
73 73
     }
74 74
 
75 75
     @SuppressLint("MissingSuperCall")
76 76
     @Override
77 77
     public void onViewDisappear() {
78
-        view.sendComponentStop();
78
+        getView().sendComponentStop();
79
+    }
80
+
81
+    @Override
82
+    public boolean isRendered() {
83
+        return !button.component.componentId.hasValue() || super.isRendered();
79 84
     }
80 85
 
81 86
     @Override

+ 2
- 2
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarReactViewController.java Целия файл

@@ -22,12 +22,12 @@ public class TitleBarReactViewController extends ViewController<TitleBarReactVie
22 22
     public void onViewAppeared() {
23 23
         super.onViewAppeared();
24 24
         runOnPreDraw(view -> view.setLayoutParams(view.getLayoutParams()));
25
-        view.sendComponentStart();
25
+        getView().sendComponentStart();
26 26
     }
27 27
 
28 28
     @Override
29 29
     public void onViewDisappear() {
30
-        view.sendComponentStop();
30
+        getView().sendComponentStop();
31 31
         super.onViewDisappear();
32 32
     }
33 33
 

+ 3
- 2
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java Целия файл

@@ -22,6 +22,7 @@ import com.reactnativenavigation.utils.UiThread;
22 22
 import com.reactnativenavigation.utils.UiUtils;
23 23
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
24 24
 import com.reactnativenavigation.views.Component;
25
+import com.reactnativenavigation.views.Renderable;
25 26
 import com.reactnativenavigation.views.element.Element;
26 27
 
27 28
 import java.util.Collections;
@@ -286,8 +287,8 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
286 287
     public boolean isRendered() {
287 288
         return view != null && (
288 289
                 waitForRender.isFalseOrUndefined() ||
289
-                !(view instanceof Component) ||
290
-                ((Component) view).isRendered()
290
+                !(view instanceof Renderable) ||
291
+                ((Renderable) view).isRendered()
291 292
         );
292 293
     }
293 294
 

+ 10
- 5
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java Целия файл

@@ -12,6 +12,7 @@ import com.reactnativenavigation.presentation.OverlayManager;
12 12
 import com.reactnativenavigation.presentation.Presenter;
13 13
 import com.reactnativenavigation.react.EventEmitter;
14 14
 import com.reactnativenavigation.utils.CommandListener;
15
+import com.reactnativenavigation.utils.CommandListenerAdapter;
15 16
 import com.reactnativenavigation.utils.CompatUtils;
16 17
 import com.reactnativenavigation.utils.Functions.Func1;
17 18
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
@@ -126,12 +127,16 @@ public class Navigator extends ParentController {
126 127
 
127 128
     public void setRoot(final ViewController viewController, CommandListener commandListener) {
128 129
         destroyRoot();
129
-        if (isRootNotCreated()) {
130
-            removePreviousContentView();
131
-            getView();
132
-        }
130
+        final boolean removeSplashView = isRootNotCreated();
131
+        if (isRootNotCreated()) getView();
133 132
         root = viewController;
134
-        rootPresenter.setRoot(root, defaultOptions, commandListener);
133
+        rootPresenter.setRoot(root, defaultOptions, new CommandListenerAdapter(commandListener) {
134
+            @Override
135
+            public void onSuccess(String childId) {
136
+                if (removeSplashView) removePreviousContentView();
137
+                super.onSuccess(childId);
138
+            }
139
+        });
135 140
     }
136 141
 
137 142
     private void removePreviousContentView() {

+ 15
- 2
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenter.java Целия файл

@@ -13,7 +13,7 @@ public class RootPresenter {
13 13
     private NavigationAnimator animator;
14 14
     private FrameLayout rootLayout;
15 15
 
16
-    public void setRootContainer(FrameLayout rootLayout) {
16
+    void setRootContainer(FrameLayout rootLayout) {
17 17
         this.rootLayout = rootLayout;
18 18
     }
19 19
 
@@ -25,9 +25,22 @@ public class RootPresenter {
25 25
         this.animator = animator;
26 26
     }
27 27
 
28
-    public void setRoot(ViewController root, Options defaultOptions, CommandListener listener) {
28
+    void setRoot(ViewController root, Options defaultOptions, CommandListener listener) {
29 29
         rootLayout.addView(root.getView());
30 30
         Options options = root.resolveCurrentOptions(defaultOptions);
31
+        root.setWaitForRender(options.animations.setRoot.waitForRender);
32
+        if (options.animations.setRoot.waitForRender.isTrue()) {
33
+            root.getView().setAlpha(0);
34
+            root.setOnAppearedListener(() -> {
35
+                root.getView().setAlpha(1);
36
+                animateSetRootAndReportSuccess(root, listener, options);
37
+            });
38
+        } else {
39
+            animateSetRootAndReportSuccess(root, listener, options);
40
+        }
41
+    }
42
+
43
+    private void animateSetRootAndReportSuccess(ViewController root, CommandListener listener, Options options) {
31 44
         if (options.animations.setRoot.hasAnimation()) {
32 45
             animator.setRoot(root.getView(), options.animations.setRoot, () -> listener.onSuccess(root.getId()));
33 46
         } else {

+ 12
- 14
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java Целия файл

@@ -21,7 +21,6 @@ import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
21 21
 import com.reactnativenavigation.viewcontrollers.IdStack;
22 22
 import com.reactnativenavigation.viewcontrollers.ParentController;
23 23
 import com.reactnativenavigation.viewcontrollers.ViewController;
24
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
25 24
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
26 25
 import com.reactnativenavigation.views.Component;
27 26
 import com.reactnativenavigation.views.ReactComponent;
@@ -38,15 +37,13 @@ public class StackController extends ParentController<StackLayout> {
38 37
 
39 38
     private final IdStack<ViewController> stack = new IdStack<>();
40 39
     private final NavigationAnimator animator;
41
-    private TopBarBackgroundViewController topBarBackgroundViewController;
42 40
     private TopBarController topBarController;
43 41
     private BackButtonHelper backButtonHelper;
44 42
     private final StackPresenter presenter;
45 43
 
46
-    public StackController(Activity activity, List<ViewController> children, ChildControllersRegistry childRegistry, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, NavigationAnimator animator, String id, Options initialOptions, BackButtonHelper backButtonHelper, StackPresenter stackPresenter, Presenter presenter) {
44
+    public StackController(Activity activity, List<ViewController> children, ChildControllersRegistry childRegistry, TopBarController topBarController, NavigationAnimator animator, String id, Options initialOptions, BackButtonHelper backButtonHelper, StackPresenter stackPresenter, Presenter presenter) {
47 45
         super(activity, childRegistry, id, presenter, initialOptions);
48 46
         this.topBarController = topBarController;
49
-        this.topBarBackgroundViewController = topBarBackgroundViewController;
50 47
         this.animator = animator;
51 48
         this.backButtonHelper = backButtonHelper;
52 49
         this.presenter = stackPresenter;
@@ -58,6 +55,16 @@ public class StackController extends ParentController<StackLayout> {
58 55
         }
59 56
     }
60 57
 
58
+    @Override
59
+    public boolean isRendered() {
60
+        if (isEmpty()) return false;
61
+        ViewGroup currentChild = getCurrentChild().getView();
62
+        if (currentChild instanceof Component) {
63
+            return super.isRendered() && presenter.isRendered((Component) currentChild);
64
+        }
65
+        return super.isRendered();
66
+    }
67
+
61 68
     @Override
62 69
     public void setDefaultOptions(Options defaultOptions) {
63 70
         super.setDefaultOptions(defaultOptions);
@@ -303,11 +310,6 @@ public class StackController extends ParentController<StackLayout> {
303 310
         pop(mergeOptions, listener);
304 311
     }
305 312
 
306
-    private void removeAndDestroyController(ViewController controller) {
307
-        stack.remove(controller.getId());
308
-        controller.destroy();
309
-    }
310
-
311 313
     ViewController peek() {
312 314
         return stack.peek();
313 315
     }
@@ -337,11 +339,7 @@ public class StackController extends ParentController<StackLayout> {
337 339
     @NonNull
338 340
     @Override
339 341
     protected StackLayout createView() {
340
-        StackLayout stackLayout = new StackLayout(getActivity(),
341
-                topBarBackgroundViewController,
342
-                topBarController,
343
-                getId()
344
-        );
342
+        StackLayout stackLayout = new StackLayout(getActivity(), topBarController, getId());
345 343
         presenter.bindView(topBarController.getView());
346 344
         addInitialChild(stackLayout);
347 345
         return stackLayout;

+ 1
- 16
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java Целия файл

@@ -7,9 +7,7 @@ import com.reactnativenavigation.parse.Options;
7 7
 import com.reactnativenavigation.presentation.Presenter;
8 8
 import com.reactnativenavigation.presentation.StackPresenter;
9 9
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
10
-import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
11 10
 import com.reactnativenavigation.viewcontrollers.ViewController;
12
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
13 11
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
14 12
 import com.reactnativenavigation.views.element.ElementTransitionManager;
15 13
 
@@ -19,8 +17,6 @@ import java.util.List;
19 17
 public class StackControllerBuilder {
20 18
     private Activity activity;
21 19
     private ChildControllersRegistry childRegistry;
22
-    private ReactViewCreator topBarButtonCreator;
23
-    private TopBarBackgroundViewController topBarBackgroundViewController;
24 20
     private TopBarController topBarController;
25 21
     private String id;
26 22
     private Options initialOptions = new Options();
@@ -56,16 +52,6 @@ public class StackControllerBuilder {
56 52
         return this;
57 53
     }
58 54
 
59
-    public StackControllerBuilder setTopBarButtonCreator(ReactViewCreator topBarButtonCreator) {
60
-        this.topBarButtonCreator = topBarButtonCreator;
61
-        return this;
62
-    }
63
-
64
-    public StackControllerBuilder setTopBarBackgroundViewController(TopBarBackgroundViewController topBarBackgroundViewController) {
65
-        this.topBarBackgroundViewController = topBarBackgroundViewController;
66
-        return this;
67
-    }
68
-
69 55
     public StackControllerBuilder setTopBarController(TopBarController topBarController) {
70 56
         this.topBarController = topBarController;
71 57
         return this;
@@ -86,7 +72,7 @@ public class StackControllerBuilder {
86 72
         return this;
87 73
     }
88 74
 
89
-    public StackControllerBuilder setBackButtonHelper(BackButtonHelper backButtonHelper) {
75
+    StackControllerBuilder setBackButtonHelper(BackButtonHelper backButtonHelper) {
90 76
         this.backButtonHelper = backButtonHelper;
91 77
         return this;
92 78
     }
@@ -95,7 +81,6 @@ public class StackControllerBuilder {
95 81
         return new StackController(activity,
96 82
                 children,
97 83
                 childRegistry,
98
-                topBarBackgroundViewController,
99 84
                 topBarController,
100 85
                 animator,
101 86
                 id,

+ 2
- 7
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarBackgroundViewController.java Целия файл

@@ -20,11 +20,6 @@ public class TopBarBackgroundViewController extends ViewController<TopBarBackgro
20 20
         this.viewCreator = viewCreator;
21 21
     }
22 22
 
23
-    public TopBarBackgroundViewController(TopBarBackgroundViewController controller) {
24
-        super(controller.getActivity(), controller.getId(), new YellowBoxDelegate(), controller.options);
25
-        this.viewCreator = controller.viewCreator;
26
-    }
27
-
28 23
     @Override
29 24
     protected TopBarBackgroundView createView() {
30 25
         return viewCreator.create(getActivity(), component.componentId.get(), component.name.get());
@@ -33,12 +28,12 @@ public class TopBarBackgroundViewController extends ViewController<TopBarBackgro
33 28
     @Override
34 29
     public void onViewAppeared() {
35 30
         super.onViewAppeared();
36
-        view.sendComponentStart();
31
+        getView().sendComponentStart();
37 32
     }
38 33
 
39 34
     @Override
40 35
     public void onViewDisappear() {
41
-        view.sendComponentStop();
36
+        getView().sendComponentStop();
42 37
         super.onViewDisappear();
43 38
     }
44 39
 

+ 4
- 4
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarController.java Целия файл

@@ -12,16 +12,16 @@ import com.reactnativenavigation.views.topbar.TopBar;
12 12
 public class TopBarController {
13 13
     private TopBar topBar;
14 14
 
15
-    public View createView(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
15
+    public View createView(Context context, StackLayout stackLayout) {
16 16
         if (topBar == null) {
17
-            topBar = createTopBar(context, topBarBackgroundViewController, stackLayout);
17
+            topBar = createTopBar(context, stackLayout);
18 18
             topBar.setId(CompatUtils.generateViewId());
19 19
         }
20 20
         return topBar;
21 21
     }
22 22
 
23
-    protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
24
-        return new TopBar(context, topBarBackgroundViewController, stackLayout);
23
+    protected TopBar createTopBar(Context context, StackLayout stackLayout) {
24
+        return new TopBar(context, stackLayout);
25 25
     }
26 26
 
27 27
     public void clear() {

+ 1
- 3
lib/android/app/src/main/java/com/reactnativenavigation/views/Component.java Целия файл

@@ -2,10 +2,8 @@ package com.reactnativenavigation.views;
2 2
 
3 3
 import com.reactnativenavigation.views.topbar.TopBar;
4 4
 
5
-public interface Component {
5
+public interface Component extends Renderable {
6 6
     void drawBehindTopBar();
7 7
 
8 8
     void drawBelowTopBar(TopBar topBar);
9
-
10
-    boolean isRendered();
11 9
 }

+ 5
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/Renderable.java Целия файл

@@ -0,0 +1,5 @@
1
+package com.reactnativenavigation.views;
2
+
3
+public interface Renderable {
4
+    boolean isRendered();
5
+}

+ 4
- 5
lib/android/app/src/main/java/com/reactnativenavigation/views/StackLayout.java Целия файл

@@ -5,7 +5,6 @@ import android.content.Context;
5 5
 import android.widget.RelativeLayout;
6 6
 
7 7
 import com.reactnativenavigation.utils.UiUtils;
8
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
9 8
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
10 9
 import com.reactnativenavigation.views.topbar.TopBar;
11 10
 
@@ -15,15 +14,15 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
15 14
 public class StackLayout extends RelativeLayout implements Component {
16 15
     private String stackId;
17 16
 
18
-    public StackLayout(Context context, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, String stackId) {
17
+    public StackLayout(Context context, TopBarController topBarController, String stackId) {
19 18
         super(context);
20 19
         this.stackId = stackId;
21
-        createLayout(topBarBackgroundViewController, topBarController);
20
+        createLayout(topBarController);
22 21
         setContentDescription("StackLayout");
23 22
     }
24 23
 
25
-    private void createLayout(TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController) {
26
-        addView(topBarController.createView(getContext(), topBarBackgroundViewController, this),
24
+    private void createLayout(TopBarController topBarController) {
25
+        addView(topBarController.createView(getContext(), this),
27 26
                 MATCH_PARENT,
28 27
                 UiUtils.getTopBarHeight(getContext())
29 28
         );

+ 9
- 13
lib/android/app/src/main/java/com/reactnativenavigation/views/topbar/TopBar.java Целия файл

@@ -27,13 +27,11 @@ import com.reactnativenavigation.anim.TopBarCollapseBehavior;
27 27
 import com.reactnativenavigation.interfaces.ScrollEventListener;
28 28
 import com.reactnativenavigation.parse.Alignment;
29 29
 import com.reactnativenavigation.parse.AnimationOptions;
30
-import com.reactnativenavigation.parse.Component;
31 30
 import com.reactnativenavigation.parse.params.Colour;
32 31
 import com.reactnativenavigation.parse.params.Number;
33 32
 import com.reactnativenavigation.utils.CompatUtils;
34 33
 import com.reactnativenavigation.utils.UiUtils;
35 34
 import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
36
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
37 35
 import com.reactnativenavigation.views.StackLayout;
38 36
 import com.reactnativenavigation.views.titlebar.TitleBar;
39 37
 import com.reactnativenavigation.views.toptabs.TopTabs;
@@ -50,14 +48,13 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
50 48
     private TopBarAnimator animator;
51 49
     private TopTabs topTabs;
52 50
     private FrameLayout root;
53
-    private TopBarBackgroundViewController topBarBackgroundViewController;
54 51
     private View border;
52
+    private View component;
55 53
 
56
-    public TopBar(final Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout parentView) {
54
+    public TopBar(final Context context, StackLayout parentView) {
57 55
         super(context);
58 56
         context.setTheme(R.style.TopBar);
59 57
         collapsingBehavior = new TopBarCollapseBehavior(this);
60
-        this.topBarBackgroundViewController = topBarBackgroundViewController;
61 58
         topTabs = new TopTabs(getContext());
62 59
         animator = new TopBarAnimator(this, parentView.getStackId());
63 60
         createLayout();
@@ -175,12 +172,9 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
175 172
         titleBar.setComponent(component);
176 173
     }
177 174
 
178
-    public void setBackgroundComponent(Component component) {
179
-        if (component.hasValue()) {
180
-            topBarBackgroundViewController.setComponent(component);
181
-            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
182
-            root.addView(topBarBackgroundViewController.getView(), 0, lp);
183
-        }
175
+    public void setBackgroundComponent(View component) {
176
+        this.component = component;
177
+        root.addView(component, 0);
184 178
     }
185 179
 
186 180
     public void setTopTabFontFamily(int tabIndex, Typeface fontFamily) {
@@ -272,8 +266,10 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
272 266
     }
273 267
 
274 268
     public void clear() {
275
-        topBarBackgroundViewController.destroy();
276
-        topBarBackgroundViewController = new TopBarBackgroundViewController(topBarBackgroundViewController);
269
+        if (component != null) {
270
+            root.removeView(component);
271
+            component = null;
272
+        }
277 273
         titleBar.clear();
278 274
     }
279 275
 

+ 4
- 6
lib/android/app/src/test/java/com/reactnativenavigation/TestUtils.java Целия файл

@@ -8,13 +8,13 @@ import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
8 8
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
9 9
 import com.reactnativenavigation.parse.Options;
10 10
 import com.reactnativenavigation.parse.params.Bool;
11
+import com.reactnativenavigation.presentation.RenderChecker;
11 12
 import com.reactnativenavigation.presentation.StackPresenter;
12 13
 import com.reactnativenavigation.utils.ImageLoader;
13 14
 import com.reactnativenavigation.utils.UiUtils;
14 15
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
15 16
 import com.reactnativenavigation.viewcontrollers.ViewController;
16 17
 import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
17
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
18 18
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
19 19
 import com.reactnativenavigation.views.StackLayout;
20 20
 import com.reactnativenavigation.views.topbar.TopBar;
@@ -24,17 +24,15 @@ public class TestUtils {
24 24
         return new StackControllerBuilder(activity)
25 25
                 .setId("stack")
26 26
                 .setChildRegistry(new ChildControllersRegistry())
27
-                .setTopBarButtonCreator(new TopBarButtonCreatorMock())
28
-                .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
29 27
                 .setTopBarController(new TopBarController() {
30 28
                     @Override
31
-                    protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
32
-                        TopBar topBar = super.createTopBar(context, topBarBackgroundViewController, stackLayout);
29
+                    protected TopBar createTopBar(Context context, StackLayout stackLayout) {
30
+                        TopBar topBar = super.createTopBar(context, stackLayout);
33 31
                         topBar.layout(0, 0, 1000, UiUtils.getTopBarHeight(context));
34 32
                         return topBar;
35 33
                     }
36 34
                 })
37
-                .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new Options())                )
35
+                .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarBackgroundViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new RenderChecker(), new Options()))
38 36
                 .setInitialOptions(new Options());
39 37
     }
40 38
 

+ 58
- 0
lib/android/app/src/test/java/com/reactnativenavigation/presentation/RenderCheckerTest.java Целия файл

@@ -0,0 +1,58 @@
1
+package com.reactnativenavigation.presentation;
2
+
3
+import com.reactnativenavigation.BaseTest;
4
+import com.reactnativenavigation.viewcontrollers.ViewController;
5
+
6
+import org.junit.Test;
7
+import org.mockito.Mockito;
8
+
9
+import java.util.Arrays;
10
+import java.util.Collection;
11
+
12
+import static com.reactnativenavigation.utils.CollectionUtils.forEach;
13
+import static org.assertj.core.api.Java6Assertions.assertThat;
14
+import static org.mockito.Mockito.verify;
15
+import static org.mockito.Mockito.when;
16
+
17
+public class RenderCheckerTest extends BaseTest {
18
+    private RenderChecker uut;
19
+
20
+    @Override
21
+    public void beforeEach() {
22
+        uut = new RenderChecker();
23
+    }
24
+
25
+    @Test
26
+    public void areRendered() {
27
+        Collection<ViewController> items = Arrays.asList(
28
+                renderedComponent(),
29
+                renderedComponent(),
30
+                renderedComponent()
31
+        );
32
+        assertThat(uut.areRendered(items)).isTrue();
33
+        forEach(items, i -> verify(i).isRendered());
34
+    }
35
+
36
+    @Test
37
+    public void areRendered_reduce() {
38
+        Collection<ViewController> items = Arrays.asList(
39
+                renderedComponent(),
40
+                notRenderedComponent(),
41
+                renderedComponent()
42
+        );
43
+        assertThat(uut.areRendered(items)).isFalse();
44
+
45
+    }
46
+
47
+    private ViewController renderedComponent() {
48
+        ViewController mock = Mockito.mock(ViewController.class);
49
+        when(mock.isRendered()).then(__ -> true);
50
+        return mock;
51
+    }
52
+
53
+    private ViewController notRenderedComponent() {
54
+        ViewController mock = Mockito.mock(ViewController.class);
55
+        when(mock.isRendered()).then(__ -> false);
56
+        return mock;
57
+    }
58
+}

+ 4
- 6
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java Целия файл

@@ -21,12 +21,12 @@ import com.reactnativenavigation.parse.params.Fraction;
21 21
 import com.reactnativenavigation.parse.params.Text;
22 22
 import com.reactnativenavigation.presentation.ComponentPresenter;
23 23
 import com.reactnativenavigation.presentation.Presenter;
24
+import com.reactnativenavigation.presentation.RenderChecker;
24 25
 import com.reactnativenavigation.presentation.StackPresenter;
25 26
 import com.reactnativenavigation.utils.CommandListenerAdapter;
26 27
 import com.reactnativenavigation.utils.ImageLoader;
27 28
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
28 29
 import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
29
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
30 30
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
31 31
 import com.reactnativenavigation.views.StackLayout;
32 32
 import com.reactnativenavigation.views.topbar.TopBar;
@@ -72,8 +72,8 @@ public class OptionsApplyingTest extends BaseTest {
72 72
         };
73 73
         TopBarController topBarController = new TopBarController() {
74 74
             @Override
75
-            protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
76
-                topBar = spy(super.createTopBar(context, topBarBackgroundViewController, stackLayout));
75
+            protected TopBar createTopBar(Context context, StackLayout stackLayout) {
76
+                topBar = spy(super.createTopBar(context, stackLayout));
77 77
                 return topBar;
78 78
             }
79 79
         };
@@ -102,12 +102,10 @@ public class OptionsApplyingTest extends BaseTest {
102 102
         uut.options.topBar.title.text = new Text("the title");
103 103
         StackController stackController =
104 104
                 new StackControllerBuilder(activity)
105
-                        .setTopBarButtonCreator(new TopBarButtonCreatorMock())
106
-                        .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
107 105
                         .setTopBarController(new TopBarController())
108 106
                         .setId("stackId")
109 107
                         .setInitialOptions(new Options())
110
-                        .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new Options()))
108
+                        .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarBackgroundViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new RenderChecker(), new Options()))
111 109
                         .build();
112 110
         stackController.ensureViewIsCreated();
113 111
         stackController.push(uut, new CommandListenerAdapter());

+ 24
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackPresenterTest.java Целия файл

@@ -13,6 +13,7 @@ import com.reactnativenavigation.mocks.ImageLoaderMock;
13 13
 import com.reactnativenavigation.mocks.TestComponentLayout;
14 14
 import com.reactnativenavigation.mocks.TestReactView;
15 15
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
16
+import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
16 17
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
17 18
 import com.reactnativenavigation.parse.Alignment;
18 19
 import com.reactnativenavigation.parse.Component;
@@ -26,6 +27,7 @@ import com.reactnativenavigation.parse.params.Colour;
26 27
 import com.reactnativenavigation.parse.params.Fraction;
27 28
 import com.reactnativenavigation.parse.params.Number;
28 29
 import com.reactnativenavigation.parse.params.Text;
30
+import com.reactnativenavigation.presentation.RenderChecker;
29 31
 import com.reactnativenavigation.presentation.StackPresenter;
30 32
 import com.reactnativenavigation.utils.TitleBarHelper;
31 33
 import com.reactnativenavigation.views.titlebar.TitleBarReactView;
@@ -36,6 +38,7 @@ import org.junit.Test;
36 38
 import org.mockito.ArgumentCaptor;
37 39
 
38 40
 import java.util.ArrayList;
41
+import java.util.Collection;
39 42
 import java.util.Collections;
40 43
 import java.util.List;
41 44
 
@@ -59,6 +62,7 @@ public class StackPresenterTest extends BaseTest {
59 62
     private TestComponentLayout otherChild;
60 63
     private Activity activity;
61 64
     private TopBar topBar;
65
+    private RenderChecker renderChecker;
62 66
 
63 67
     private Button textBtn1 = TitleBarHelper.textualButton("btn1");
64 68
     private Button textBtn2 = TitleBarHelper.textualButton("btn2");
@@ -82,7 +86,8 @@ public class StackPresenterTest extends BaseTest {
82 86
                 return spy(super.create(activity, componentId, componentName));
83 87
             }
84 88
         };
85
-        uut = spy(new StackPresenter(activity, titleViewCreator, new TopBarButtonCreatorMock(), ImageLoaderMock.mock(), new Options()));
89
+        renderChecker = spy(new RenderChecker());
90
+        uut = spy(new StackPresenter(activity, titleViewCreator, new TopBarBackgroundViewCreatorMock(), new TopBarButtonCreatorMock(), ImageLoaderMock.mock(), renderChecker, new Options()));
86 91
         topBar = mockTopBar();
87 92
         uut.bindView(topBar);
88 93
         uut.setButtonOnClickListener(onClickListener);
@@ -90,6 +95,24 @@ public class StackPresenterTest extends BaseTest {
90 95
         otherChild = new TestComponentLayout(activity, new TestReactView(activity));
91 96
     }
92 97
 
98
+    @Test
99
+    public void isRendered() {
100
+        Options o1 = new Options();
101
+        o1.topBar.title.component = component(Alignment.Default);
102
+        o1.topBar.background.component = component(Alignment.Default);
103
+        o1.topBar.buttons.right = new ArrayList(Collections.singletonList(componentBtn1));
104
+        uut.applyChildOptions(o1, child);
105
+
106
+        uut.isRendered(child);
107
+        ArgumentCaptor<Collection<ViewController>> controllers = ArgumentCaptor.forClass(Collection.class);
108
+        verify(renderChecker).areRendered(controllers.capture());
109
+        ArrayList<ViewController> items = new ArrayList(controllers.getValue());
110
+        assertThat(items.contains(uut.getComponentButtons(child).get(0))).isTrue();
111
+        assertThat(items.contains(uut.getTitleComponents().get(child))).isTrue();
112
+        assertThat(items.contains(uut.getBackgroundComponents().get(child))).isTrue();
113
+        assertThat(items.size()).isEqualTo(3);
114
+    }
115
+
93 116
     @Test
94 117
     public void applyChildOptions_setTitleComponent() {
95 118
         Options options = new Options();

+ 2
- 5
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopBarButtonControllerTest.java Целия файл

@@ -30,6 +30,7 @@ import static org.mockito.Mockito.spy;
30 30
 import static org.mockito.Mockito.times;
31 31
 import static org.mockito.Mockito.verify;
32 32
 
33
+@SuppressWarnings("MagicNumber")
33 34
 public class TopBarButtonControllerTest extends BaseTest {
34 35
 
35 36
     private TitleBarButtonController uut;
@@ -43,11 +44,7 @@ public class TopBarButtonControllerTest extends BaseTest {
43 44
         final Activity activity = newActivity();
44 45
 
45 46
         TopBarButtonCreatorMock buttonCreatorMock = new TopBarButtonCreatorMock();
46
-        stackController = spy(
47
-                TestUtils.newStackController(activity)
48
-                        .setTopBarButtonCreator(buttonCreatorMock)
49
-                        .build()
50
-        );
47
+        stackController = spy(TestUtils.newStackController(activity).build());
51 48
         stackController.getView().layout(0, 0, 1080, 1920);
52 49
         stackController.getTopBar().layout(0, 0, 1080, 200);
53 50
         getTitleBar().layout(0, 0, 1080, 200);

+ 3
- 5
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopBarControllerTest.java Целия файл

@@ -5,8 +5,6 @@ import android.content.Context;
5 5
 import android.support.annotation.NonNull;
6 6
 
7 7
 import com.reactnativenavigation.BaseTest;
8
-import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
9
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
10 8
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
11 9
 import com.reactnativenavigation.views.StackLayout;
12 10
 import com.reactnativenavigation.views.titlebar.TitleBar;
@@ -34,8 +32,8 @@ public class TopBarControllerTest extends BaseTest {
34 32
         uut = new TopBarController() {
35 33
             @NonNull
36 34
             @Override
37
-            protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
38
-                return new TopBar(context, topBarBackgroundViewController, stackLayout) {
35
+            protected TopBar createTopBar(Context context, StackLayout stackLayout) {
36
+                return new TopBar(context, stackLayout) {
39 37
                     @Override
40 38
                     protected TitleBar createTitleBar(Context context) {
41 39
                         titleBar[0] = spy(super.createTitleBar(context));
@@ -45,7 +43,7 @@ public class TopBarControllerTest extends BaseTest {
45 43
             }
46 44
         };
47 45
         Activity activity = newActivity();
48
-        uut.createView(activity, new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()), Mockito.mock(StackLayout.class));
46
+        uut.createView(activity, Mockito.mock(StackLayout.class));
49 47
         uut.clear();
50 48
         verify(titleBar[0], times(1)).clear();
51 49
     }

+ 5
- 3
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/NavigatorTest.java Целия файл

@@ -34,6 +34,7 @@ import com.reactnativenavigation.viewcontrollers.stack.StackController;
34 34
 import com.reactnativenavigation.views.BottomTabs;
35 35
 
36 36
 import org.junit.Test;
37
+import org.mockito.ArgumentCaptor;
37 38
 import org.mockito.Mockito;
38 39
 import org.robolectric.android.controller.ActivityController;
39 40
 import org.robolectric.annotation.Config;
@@ -44,6 +45,7 @@ import java.util.List;
44 45
 
45 46
 import static org.assertj.core.api.Java6Assertions.assertThat;
46 47
 import static org.mockito.ArgumentMatchers.any;
48
+import static org.mockito.ArgumentMatchers.eq;
47 49
 import static org.mockito.Mockito.spy;
48 50
 import static org.mockito.Mockito.times;
49 51
 import static org.mockito.Mockito.verify;
@@ -131,13 +133,13 @@ public class NavigatorTest extends BaseTest {
131 133
     public void setRoot_delegatesToRootPresenter() {
132 134
         CommandListenerAdapter listener = new CommandListenerAdapter();
133 135
         uut.setRoot(child1, listener);
134
-        verify(rootPresenter).setRoot(child1, uut.getDefaultOptions(), listener);
136
+        ArgumentCaptor<CommandListenerAdapter> captor = ArgumentCaptor.forClass(CommandListenerAdapter.class);
137
+        verify(rootPresenter).setRoot(eq(child1), eq(uut.getDefaultOptions()), captor.capture());
138
+        assertThat(captor.getValue().getListener()).isEqualTo(listener);
135 139
     }
136 140
 
137 141
     @Test
138 142
     public void setRoot_clearsSplashLayout() {
139
-        disableModalAnimations(child1);
140
-
141 143
         FrameLayout content = activity.findViewById(android.R.id.content);
142 144
         assertThat(content.getChildCount()).isEqualTo(4); // 3 frame layouts and the default splash layout
143 145
 

+ 30
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenterTest.java Целия файл

@@ -10,12 +10,14 @@ import com.reactnativenavigation.anim.NavigationAnimator;
10 10
 import com.reactnativenavigation.mocks.SimpleViewController;
11 11
 import com.reactnativenavigation.parse.AnimationOptions;
12 12
 import com.reactnativenavigation.parse.Options;
13
+import com.reactnativenavigation.parse.params.Bool;
13 14
 import com.reactnativenavigation.utils.CommandListenerAdapter;
14 15
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
15 16
 import com.reactnativenavigation.viewcontrollers.ViewController;
16 17
 import com.reactnativenavigation.views.element.ElementTransitionManager;
17 18
 
18 19
 import org.junit.Test;
20
+import org.mockito.ArgumentCaptor;
19 21
 
20 22
 import static org.assertj.core.api.Java6Assertions.assertThat;
21 23
 import static org.mockito.ArgumentMatchers.any;
@@ -85,6 +87,34 @@ public class RootPresenterTest extends BaseTest {
85 87
         verify(listener).onSuccess(spy.getId());
86 88
     }
87 89
 
90
+    @Test
91
+    public void setRoot_waitForRenderIsSet() {
92
+        root.options.animations.setRoot.waitForRender = new Bool(true);
93
+        ViewController spy = spy(root);
94
+
95
+        uut.setRoot(spy, defaultOptions, new CommandListenerAdapter());
96
+
97
+        ArgumentCaptor<Bool> captor = ArgumentCaptor.forClass(Bool.class);
98
+        verify(spy).setWaitForRender(captor.capture());
99
+        assertThat(captor.getValue().get()).isTrue();
100
+    }
101
+
102
+    @Test
103
+    public void setRoot_waitForRender() {
104
+        root.options.animations.setRoot.waitForRender = new Bool(true);
105
+
106
+        ViewController spy = spy(root);
107
+        CommandListenerAdapter listener = spy(new CommandListenerAdapter());
108
+        uut.setRoot(spy, defaultOptions, listener);
109
+        verify(spy).setOnAppearedListener(any());
110
+        assertThat(spy.getView().getAlpha()).isZero();
111
+        verifyZeroInteractions(listener);
112
+
113
+        spy.onViewAppeared();
114
+        assertThat(spy.getView().getAlpha()).isOne();
115
+        verify(listener).onSuccess(spy.getId());
116
+    }
117
+
88 118
     @NonNull
89 119
     private NavigationAnimator createAnimator(Activity activity) {
90 120
         return new NavigationAnimator(activity, mock(ElementTransitionManager.class)) {

+ 34
- 13
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.java Целия файл

@@ -20,6 +20,7 @@ import com.reactnativenavigation.parse.NestedAnimationsOptions;
20 20
 import com.reactnativenavigation.parse.Options;
21 21
 import com.reactnativenavigation.parse.params.Bool;
22 22
 import com.reactnativenavigation.parse.params.Text;
23
+import com.reactnativenavigation.presentation.RenderChecker;
23 24
 import com.reactnativenavigation.presentation.StackPresenter;
24 25
 import com.reactnativenavigation.utils.CommandListenerAdapter;
25 26
 import com.reactnativenavigation.utils.ImageLoader;
@@ -30,7 +31,6 @@ import com.reactnativenavigation.utils.ViewUtils;
30 31
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
31 32
 import com.reactnativenavigation.viewcontrollers.ParentController;
32 33
 import com.reactnativenavigation.viewcontrollers.ViewController;
33
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
34 34
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
35 35
 import com.reactnativenavigation.views.Component;
36 36
 import com.reactnativenavigation.views.ReactComponent;
@@ -74,6 +74,7 @@ public class StackControllerTest extends BaseTest {
74 74
     private TopBarController topBarController;
75 75
     private StackPresenter presenter;
76 76
     private BackButtonHelper backButtonHelper;
77
+    private RenderChecker renderChecker;
77 78
 
78 79
     @Override
79 80
     public void beforeEach() {
@@ -82,7 +83,8 @@ public class StackControllerTest extends BaseTest {
82 83
         activity = newActivity();
83 84
         animator = spy(new NavigationAnimator(activity, Mockito.mock(ElementTransitionManager.class)));
84 85
         childRegistry = new ChildControllersRegistry();
85
-        presenter = spy(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), ImageLoaderMock.mock(), new Options()));
86
+        renderChecker = spy(new RenderChecker());
87
+        presenter = spy(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarBackgroundViewCreatorMock(), new TopBarButtonCreatorMock(), ImageLoaderMock.mock(), renderChecker, new Options()));
86 88
         child1 = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
87 89
         child2 = spy(new SimpleViewController(activity, childRegistry, "child2", new Options()));
88 90
         child3 = spy(new SimpleViewController(activity, childRegistry, "child3", new Options()));
@@ -129,6 +131,31 @@ public class StackControllerTest extends BaseTest {
129 131
         assertContainsOnlyId(child1.getId(), child2.getId(), child3.getId());
130 132
     }
131 133
 
134
+    @Test
135
+    public void isRendered_falseIfStackIsEmpty() {
136
+        assertThat(uut.size()).isZero();
137
+        assertThat(uut.isRendered()).isFalse();
138
+    }
139
+
140
+    @Test
141
+    public void isRendered() {
142
+        disablePushAnimation(child1);
143
+
144
+        uut.push(child1, new CommandListenerAdapter());
145
+        verify(presenter).isRendered((Component) child1.getView());
146
+        verify(renderChecker).areRendered(any());
147
+        assertThat(uut.isRendered()).isTrue();
148
+
149
+        child1.setWaitForRender(new Bool(true));
150
+        assertThat(uut.isRendered()).isFalse();
151
+
152
+        child1.getView().addView(new View(activity));
153
+        assertThat(uut.isRendered()).isTrue();
154
+
155
+        Mockito.when(presenter.isRendered((Component) child1.getView())).then(ignored -> false);
156
+        assertThat(uut.isRendered()).isFalse();
157
+    }
158
+
132 159
     @Test
133 160
     public void push() {
134 161
         assertThat(uut.isEmpty()).isTrue();
@@ -285,12 +312,10 @@ public class StackControllerTest extends BaseTest {
285 312
     @Test
286 313
     public void pop_layoutHandlesChildWillDisappear() {
287 314
         uut = new StackControllerBuilder(activity)
288
-                        .setTopBarButtonCreator(new TopBarButtonCreatorMock())
289
-                        .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
290 315
                         .setTopBarController(new TopBarController())
291 316
                         .setId("uut")
292 317
                         .setInitialOptions(new Options())
293
-                        .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new Options()))
318
+                        .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarBackgroundViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new RenderChecker(), new Options()))
294 319
                         .build();
295 320
         uut.ensureViewIsCreated();
296 321
         uut.push(child1, new CommandListenerAdapter());
@@ -854,12 +879,10 @@ public class StackControllerTest extends BaseTest {
854 879
     @Test
855 880
     public void mergeChildOptions_updatesViewWithNewOptions() {
856 881
         StackController uut = spy(new StackControllerBuilder(activity)
857
-                        .setTopBarButtonCreator(new TopBarButtonCreatorMock())
858
-                        .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
859 882
                         .setTopBarController(new TopBarController())
860 883
                         .setId("stack")
861 884
                         .setInitialOptions(new Options())
862
-                        .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new Options()))
885
+                        .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarBackgroundViewCreatorMock(), new TitleBarReactViewCreatorMock(), ImageLoaderMock.mock(), new RenderChecker(), Options.EMPTY))
863 886
                         .build());
864 887
         Options optionsToMerge = new Options();
865 888
         Component component = mock(Component.class);
@@ -871,12 +894,10 @@ public class StackControllerTest extends BaseTest {
871 894
     @Test
872 895
     public void mergeChildOptions_updatesParentControllerWithNewOptions() {
873 896
         StackController uut = new StackControllerBuilder(activity)
874
-                        .setTopBarButtonCreator(new TopBarButtonCreatorMock())
875
-                        .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
876 897
                         .setTopBarController(new TopBarController())
877 898
                         .setId("stack")
878 899
                         .setInitialOptions(new Options())
879
-                        .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new Options()))
900
+                        .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarBackgroundViewCreatorMock(), new TitleBarReactViewCreatorMock(), ImageLoaderMock.mock(), new RenderChecker(), Options.EMPTY))
880 901
                         .build();
881 902
         ParentController parentController = Mockito.mock(ParentController.class);
882 903
         uut.setParentController(parentController);
@@ -1006,8 +1027,8 @@ public class StackControllerTest extends BaseTest {
1006 1027
     private void createTopBarController() {
1007 1028
         topBarController = spy(new TopBarController() {
1008 1029
             @Override
1009
-            protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
1010
-                TopBar spy = spy(super.createTopBar(context, topBarBackgroundViewController, stackLayout));
1030
+            protected TopBar createTopBar(Context context, StackLayout stackLayout) {
1031
+                TopBar spy = spy(super.createTopBar(context, stackLayout));
1011 1032
                 spy.layout(0, 0, 1000, UiUtils.getTopBarHeight(activity));
1012 1033
                 return spy;
1013 1034
             }

+ 0
- 70
lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarBackgroundComponentTest.java Целия файл

@@ -1,70 +0,0 @@
1
-package com.reactnativenavigation.views;
2
-
3
-import android.app.Activity;
4
-import android.view.View;
5
-import android.view.ViewGroup;
6
-
7
-import com.reactnativenavigation.BaseTest;
8
-import com.reactnativenavigation.R;
9
-import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
10
-import com.reactnativenavigation.parse.Component;
11
-import com.reactnativenavigation.parse.params.Text;
12
-import com.reactnativenavigation.utils.ViewUtils;
13
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
14
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
15
-import com.reactnativenavigation.views.topbar.TopBar;
16
-import com.reactnativenavigation.views.topbar.TopBarBackgroundView;
17
-
18
-import org.junit.Test;
19
-
20
-import static org.assertj.core.api.Java6Assertions.assertThat;
21
-import static org.mockito.Mockito.spy;
22
-import static org.mockito.Mockito.times;
23
-import static org.mockito.Mockito.verify;
24
-
25
-public class TopBarBackgroundComponentTest extends BaseTest {
26
-    private TopBar uut;
27
-    private TopBarBackgroundViewController topBarBackgroundViewController;
28
-
29
-    @SuppressWarnings("Convert2Lambda")
30
-    @Override
31
-    public void beforeEach() {
32
-        Activity activity = newActivity();
33
-        topBarBackgroundViewController = spy(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()));
34
-        StackLayout parent = new StackLayout(activity, topBarBackgroundViewController, new TopBarController(), null);
35
-        uut = new TopBar(activity, topBarBackgroundViewController, parent);
36
-        parent.addView(uut);
37
-    }
38
-
39
-    @Test
40
-    public void setBackgroundComponent() {
41
-        uut.getLayoutParams().height = 100;
42
-        Component component = new Component();
43
-        component.name = new Text("someComponent");
44
-        component.componentId = new Text("id");
45
-        uut.setBackgroundComponent(component);
46
-        TopBarBackgroundView background = (TopBarBackgroundView) ViewUtils.findChildrenByClassRecursive(uut, TopBarBackgroundView.class).get(0);
47
-        assertThat(background).isNotNull();
48
-        assertThat(background.getLayoutParams().width).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT);
49
-        assertThat(background.getLayoutParams().height).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT);
50
-    }
51
-
52
-    @Test
53
-    public void setBackgroundComponent_doesNotSetIfNoComponentIsDefined() {
54
-        Component component = new Component();
55
-        component.name = new Text("someComponent");
56
-        component.componentId = new Text("id");
57
-        uut.setBackgroundComponent(component);
58
-        assertThat((View) uut.findViewById(R.id.topBarBackgroundComponent)).isNull();
59
-    }
60
-
61
-    @Test
62
-    public void clear_componentIsDestroyed() {
63
-        Component component = new Component();
64
-        component.name = new Text("someComponent");
65
-        component.componentId = new Text("id");
66
-        uut.setBackgroundComponent(component);
67
-        uut.clear();
68
-        verify(topBarBackgroundViewController, times(1)).destroy();
69
-    }
70
-}

+ 2
- 5
lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarTest.java Целия файл

@@ -4,9 +4,7 @@ import android.app.Activity;
4 4
 
5 5
 import com.reactnativenavigation.BaseTest;
6 6
 import com.reactnativenavigation.anim.TopBarAnimator;
7
-import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
8 7
 import com.reactnativenavigation.parse.AnimationOptions;
9
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
10 8
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
11 9
 import com.reactnativenavigation.views.topbar.TopBar;
12 10
 
@@ -28,9 +26,8 @@ public class TopBarTest extends BaseTest {
28 26
     @Override
29 27
     public void beforeEach() {
30 28
         Activity activity = newActivity();
31
-        TopBarBackgroundViewController topBarBackgroundViewController = new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock());
32
-        StackLayout parent = new StackLayout(activity, topBarBackgroundViewController, new TopBarController(), null);
33
-        uut = new TopBar(activity, topBarBackgroundViewController, parent);
29
+        StackLayout parent = new StackLayout(activity, new TopBarController(), null);
30
+        uut = new TopBar(activity, parent);
34 31
         animator = spy(new TopBarAnimator(uut));
35 32
         uut.setAnimator(animator);
36 33
         parent.addView(uut);