Browse Source

Button unmount (#3856)

Unmount buttons when corresponding screen is unmounted

Currently buttons were unmounted each time a screen was pushed to the stack.
As we're clearing props when screens are unmounted from the prop store - when a screen
was popped to - its buttons's props were undefined.

This commit changes things so that buttons are unmounted when buttons are set
using mergeOptions and when the corresponding screen is unmounted.
Guy Carmeli 6 years ago
parent
commit
68373759c0
No account linked to committer's email address
32 changed files with 504 additions and 317 deletions
  1. 1
    1
      e2e/ScreenStyle.test.js
  2. 1
    1
      e2e/init.js
  3. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java
  4. 4
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java
  5. 122
    28
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackOptionsPresenter.java
  6. 45
    7
      lib/android/app/src/main/java/com/reactnativenavigation/utils/CollectionUtils.java
  7. 9
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ChildController.java
  8. 0
    6
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java
  9. 20
    10
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarButtonController.java
  10. 1
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java
  11. 4
    7
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarController.java
  12. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentLayout.java
  13. 4
    9
      lib/android/app/src/main/java/com/reactnativenavigation/views/StackLayout.java
  14. 3
    4
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/ElementTransitionManager.java
  15. 10
    43
      lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBar.java
  16. 11
    17
      lib/android/app/src/main/java/com/reactnativenavigation/views/topbar/TopBar.java
  17. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/views/toptabs/TopTabsViewPager.java
  18. 3
    5
      lib/android/app/src/test/java/com/reactnativenavigation/TestUtils.java
  19. 2
    2
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestComponentLayout.java
  20. 45
    0
      lib/android/app/src/test/java/com/reactnativenavigation/utils/TitleBarHelper.java
  21. 2
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsControllerTest.java
  22. 3
    4
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java
  23. 160
    18
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackOptionsPresenterTest.java
  24. 11
    48
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TitleBarTest.java
  25. 2
    2
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopBarButtonControllerTest.java
  26. 5
    7
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopBarControllerTest.java
  27. 8
    13
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.java
  28. 2
    12
      lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarBackgroundComponentTest.java
  29. 2
    45
      lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarTest.java
  30. 5
    6
      lib/android/app/src/test/java/com/reactnativenavigation/views/element/TransitionAnimatorCreatorTest.java
  31. 13
    13
      lib/android/app/src/test/java/com/reactnativenavigation/views/element/TransitionValidatorTest.java
  32. 1
    1
      scripts/test-e2e.js

+ 1
- 1
e2e/ScreenStyle.test.js View File

@@ -115,7 +115,7 @@ describe('screen style', () => {
115 115
     await expect(elementByLabel(`Two`)).toExist();
116 116
   });
117 117
 
118
-  test(':ios: pass props to custom button component should exist after screen rerender', async () => {
118
+  test('pass props to custom button component should exist after push pop', async () => {
119 119
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
120 120
     await expect(elementByLabel(`Two`)).toExist();
121 121
     await elementById(testIDs.SCROLLVIEW_SCREEN_BUTTON).tap();

+ 1
- 1
e2e/init.js View File

@@ -3,7 +3,7 @@ const config = require('../package.json').detox;
3 3
 const exec = require('shell-utils').exec;
4 4
 const adapter = require('detox/runners/jest/adapter');
5 5
 
6
-jest.setTimeout(300000);
6
+jest.setTimeout(1000 * 60);
7 7
 jasmine.getEnv().addReporter(adapter); // don't forget this line
8 8
 
9 9
 beforeAll(async () => {

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java View File

@@ -166,7 +166,7 @@ public class LayoutFactory {
166 166
                 .setTopBarController(new TopBarController())
167 167
                 .setId(node.id)
168 168
                 .setInitialOptions(parse(typefaceManager, node.getOptions()))
169
-                .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreator(reactInstanceManager), defaultOptions))
169
+                .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreator(reactInstanceManager), new TitleBarButtonCreator(reactInstanceManager), new ImageLoader(), defaultOptions))
170 170
                 .setPresenter(new OptionsPresenter(activity, defaultOptions))
171 171
                 .build();
172 172
 	}

+ 4
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java View File

@@ -9,6 +9,7 @@ import com.reactnativenavigation.parse.parsers.BoolParser;
9 9
 import com.reactnativenavigation.parse.parsers.ColorParser;
10 10
 import com.reactnativenavigation.parse.parsers.NumberParser;
11 11
 import com.reactnativenavigation.parse.parsers.TextParser;
12
+import com.reactnativenavigation.utils.CompatUtils;
12 13
 import com.reactnativenavigation.utils.TypefaceLoader;
13 14
 
14 15
 import org.json.JSONArray;
@@ -17,6 +18,8 @@ import org.json.JSONObject;
17 18
 import java.util.ArrayList;
18 19
 
19 20
 public class Button {
21
+    public String instanceId = "btn" + CompatUtils.generateViewId();
22
+
20 23
     @Nullable public String id;
21 24
     public Text text = new NullText();
22 25
     public Bool enabled = new NullBool();
@@ -125,6 +128,7 @@ public class Button {
125 128
         if (other.showAsAction.hasValue()) showAsAction = other.showAsAction;
126 129
         if (other.icon.hasValue()) icon = other.icon;
127 130
         if (other.id != null) id = other.id;
131
+        if (other.instanceId != null) instanceId = other.instanceId;
128 132
     }
129 133
 
130 134
     public void mergeWithDefault(Button defaultOptions) {

+ 122
- 28
lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackOptionsPresenter.java View File

@@ -19,18 +19,28 @@ import com.reactnativenavigation.parse.TopTabOptions;
19 19
 import com.reactnativenavigation.parse.TopTabsOptions;
20 20
 import com.reactnativenavigation.parse.params.Button;
21 21
 import com.reactnativenavigation.parse.params.Colour;
22
+import com.reactnativenavigation.utils.ButtonOptionsPresenter;
23
+import com.reactnativenavigation.utils.ImageLoader;
22 24
 import com.reactnativenavigation.utils.UiUtils;
23 25
 import com.reactnativenavigation.viewcontrollers.IReactView;
26
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
27
+import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
24 28
 import com.reactnativenavigation.viewcontrollers.TitleBarReactViewController;
29
+import com.reactnativenavigation.viewcontrollers.button.NavigationIconResolver;
25 30
 import com.reactnativenavigation.views.Component;
26 31
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
27 32
 import com.reactnativenavigation.views.topbar.TopBar;
28 33
 
29 34
 import java.util.ArrayList;
35
+import java.util.Collections;
30 36
 import java.util.HashMap;
31 37
 import java.util.List;
32 38
 import java.util.Map;
33 39
 
40
+import static com.reactnativenavigation.utils.CollectionUtils.forEach;
41
+import static com.reactnativenavigation.utils.CollectionUtils.keyBy;
42
+import static com.reactnativenavigation.utils.CollectionUtils.merge;
43
+
34 44
 public class StackOptionsPresenter {
35 45
     private static final int DEFAULT_TITLE_COLOR = Color.BLACK;
36 46
     private static final int DEFAULT_SUBTITLE_COLOR = Color.GRAY;
@@ -41,13 +51,20 @@ public class StackOptionsPresenter {
41 51
     private final Activity activity;
42 52
 
43 53
     private TopBar topBar;
44
-    private TitleBarReactViewCreator titleViewCreator;
54
+    private final TitleBarReactViewCreator titleViewCreator;
55
+    private TitleBarButtonController.OnClickListener onClickListener;
56
+    private final ImageLoader imageLoader;
57
+    private final ReactViewCreator buttonCreator;
45 58
     private Options defaultOptions;
46 59
     private Map<Component, TitleBarReactViewController> titleComponentViewControllers = new HashMap<>();
60
+    private Map<Component, Map<String, TitleBarButtonController>> componentRightButtons = new HashMap<>();
61
+    private Map<Component, Map<String, TitleBarButtonController>> componentLeftButtons = new HashMap<>();
47 62
 
48
-    public StackOptionsPresenter(Activity activity, TitleBarReactViewCreator titleViewCreator, Options defaultOptions) {
63
+    public StackOptionsPresenter(Activity activity, TitleBarReactViewCreator titleViewCreator, ReactViewCreator buttonCreator, ImageLoader imageLoader, Options defaultOptions) {
49 64
         this.activity = activity;
50 65
         this.titleViewCreator = titleViewCreator;
66
+        this.buttonCreator = buttonCreator;
67
+        this.imageLoader = imageLoader;
51 68
         this.defaultOptions = defaultOptions;
52 69
         defaultTitleFontSize = UiUtils.dpToSp(activity, 18);
53 70
         defaultSubtitleFontSize = UiUtils.dpToSp(activity, 14);
@@ -57,10 +74,30 @@ public class StackOptionsPresenter {
57 74
         this.defaultOptions = defaultOptions;
58 75
     }
59 76
 
77
+    public void setButtonOnClickListener(TitleBarButtonController.OnClickListener onClickListener) {
78
+        this.onClickListener = onClickListener;
79
+    }
80
+
60 81
     public Options getDefaultOptions() {
61 82
         return defaultOptions;
62 83
     }
63 84
 
85
+    public List<TitleBarButtonController> getComponentButtons(Component child) {
86
+        return merge(getRightButtons(child), getLeftButtons(child), Collections.EMPTY_LIST);
87
+    }
88
+
89
+    public List<TitleBarButtonController> getComponentButtons(Component child, List<TitleBarButtonController> defaultValue) {
90
+        return merge(getRightButtons(child), getLeftButtons(child), defaultValue);
91
+    }
92
+
93
+    private List<TitleBarButtonController> getRightButtons(Component child) {
94
+        return componentRightButtons.containsKey(child) ? new ArrayList<>(componentRightButtons.get(child).values()) : null;
95
+    }
96
+
97
+    private List<TitleBarButtonController> getLeftButtons(Component child) {
98
+        return componentLeftButtons.containsKey(child) ? new ArrayList<>(componentLeftButtons.get(child).values()) : null;
99
+    }
100
+
64 101
     public void bindView(TopBar topBar) {
65 102
         this.topBar = topBar;
66 103
     }
@@ -80,7 +117,7 @@ public class StackOptionsPresenter {
80 117
     public void applyChildOptions(Options options, Component child) {
81 118
         Options withDefault = options.copy().withDefaultOptions(defaultOptions);
82 119
         applyOrientation(withDefault.layout.orientation);
83
-        applyButtons(withDefault.topBar, withDefault.topBar.rightButtonColor, withDefault.topBar.leftButtonColor, withDefault.topBar.rightButtonDisabledColor, withDefault.topBar.leftButtonDisabledColor);
120
+        applyButtons(withDefault.topBar, child);
84 121
         applyTopBarOptions(withDefault.topBar, withDefault.animations, child, options);
85 122
         applyTopTabsOptions(withDefault.topTabs);
86 123
         applyTopTabOptions(withDefault.topTabOptions);
@@ -96,6 +133,18 @@ public class StackOptionsPresenter {
96 133
         if (removed != null) {
97 134
             removed.destroy();
98 135
         }
136
+        destroyButtons(componentRightButtons.get(child));
137
+        destroyButtons(componentLeftButtons.get(child));
138
+        componentRightButtons.remove(child);
139
+        componentLeftButtons.remove(child);
140
+    }
141
+
142
+    private void destroyButtons(Map<String, TitleBarButtonController> buttons) {
143
+        if (buttons != null) {
144
+            for (TitleBarButtonController button : buttons.values()) {
145
+                button.destroy();
146
+            }
147
+        }
99 148
     }
100 149
 
101 150
     private void applyTopBarOptions(TopBarOptions options, AnimationsOptions animationOptions, Component component, Options componentOptions) {
@@ -166,12 +215,49 @@ public class StackOptionsPresenter {
166 215
         }
167 216
     }
168 217
 
169
-    private void applyButtons(TopBarOptions options, Colour rightButtonColor, Colour leftButtonColor, Colour rightButtonDisabledColor, Colour leftButtonDisabledColor) {
170
-        List<Button> rightButtons = mergeButtonsWithColor(options.buttons.right, rightButtonColor, rightButtonDisabledColor);
171
-        List<Button> leftButtons = mergeButtonsWithColor(options.buttons.left, leftButtonColor, leftButtonDisabledColor);
172
-        topBar.setRightButtons(rightButtons);
173
-        topBar.setLeftButtons(leftButtons);
174
-        if (options.buttons.back.visible.isTrue() && !options.buttons.hasLeftButtons()) topBar.setBackButton(options.buttons.back);
218
+    private void applyButtons(TopBarOptions options, Component child) {
219
+        List<Button> rightButtons = mergeButtonsWithColor(options.buttons.right, options.rightButtonColor, options.rightButtonDisabledColor);
220
+        List<Button> leftButtons = mergeButtonsWithColor(options.buttons.left, options.leftButtonColor, options.leftButtonDisabledColor);
221
+
222
+        if (rightButtons != null) {
223
+            List<TitleBarButtonController> rightButtonControllers = getOrCreateButtonControllers(componentRightButtons.get(child), rightButtons);
224
+            componentRightButtons.put(child, keyBy(rightButtonControllers, TitleBarButtonController::getButtonInstanceId));
225
+            topBar.setRightButtons(rightButtonControllers);
226
+        } else {
227
+            topBar.setRightButtons(null);
228
+        }
229
+
230
+        if (leftButtons != null) {
231
+            List<TitleBarButtonController> leftButtonControllers = getOrCreateButtonControllers(componentLeftButtons.get(child), leftButtons);
232
+            componentLeftButtons.put(child, keyBy(leftButtonControllers, TitleBarButtonController::getButtonInstanceId));
233
+            topBar.setLeftButtons(leftButtonControllers);
234
+        } else {
235
+            topBar.setLeftButtons(null);
236
+        }
237
+
238
+        if (options.buttons.back.visible.isTrue() && !options.buttons.hasLeftButtons()) {
239
+            topBar.setBackButton(createButtonController(options.buttons.back));
240
+        }
241
+    }
242
+
243
+    private List<TitleBarButtonController> getOrCreateButtonControllers(@Nullable Map<String, TitleBarButtonController> currentButtons, @Nullable List<Button> buttons) {
244
+        if (buttons == null) return null;
245
+        Map<String, TitleBarButtonController> result = new HashMap<>();
246
+        for (Button b : buttons) {
247
+            result.put(b.instanceId, currentButtons != null && currentButtons.containsKey(b.instanceId) ? currentButtons.get(b.instanceId) : createButtonController(b));
248
+        }
249
+        return new ArrayList<>(result.values());
250
+    }
251
+
252
+    private TitleBarButtonController createButtonController(Button button) {
253
+        return new TitleBarButtonController(activity,
254
+                new NavigationIconResolver(activity, imageLoader),
255
+                imageLoader,
256
+                new ButtonOptionsPresenter(topBar.getTitleBar(), button),
257
+                button,
258
+                buttonCreator,
259
+                onClickListener
260
+        );
175 261
     }
176 262
 
177 263
     private void applyTopTabsOptions(TopTabsOptions options) {
@@ -195,30 +281,38 @@ public class StackOptionsPresenter {
195 281
         }
196 282
     }
197 283
 
198
-    public void mergeChildOptions(Options options, Options childOptions, Component child) {
199
-        TopBarOptions topBar = options.copy().mergeWith(childOptions).withDefaultOptions(defaultOptions).topBar;
200
-        mergeOrientation(options.layout.orientation);
201
-        mergeButtons(options.topBar.buttons,
202
-                topBar.rightButtonColor,
203
-                topBar.leftButtonColor,
204
-                topBar.rightButtonDisabledColor,
205
-                topBar.leftButtonDisabledColor
206
-        );
207
-        mergeTopBarOptions(options.topBar, options.animations, child);
208
-        mergeTopTabsOptions(options.topTabs);
209
-        mergeTopTabOptions(options.topTabOptions);
284
+    public void mergeChildOptions(Options toMerge, Options resolvedOptions, Component child) {
285
+        TopBarOptions topBar = toMerge.copy().mergeWith(resolvedOptions).withDefaultOptions(defaultOptions).topBar;
286
+        mergeOrientation(toMerge.layout.orientation);
287
+        mergeButtons(topBar, toMerge.topBar.buttons, child);
288
+        mergeTopBarOptions(toMerge.topBar, toMerge.animations, child);
289
+        mergeTopTabsOptions(toMerge.topTabs);
290
+        mergeTopTabOptions(toMerge.topTabOptions);
210 291
     }
211 292
 
212 293
     private void mergeOrientation(OrientationOptions orientationOptions) {
213 294
         if (orientationOptions.hasValue()) applyOrientation(orientationOptions);
214 295
     }
215 296
 
216
-    private void mergeButtons(TopBarButtons buttons, Colour rightButtonColor, Colour leftButtonColor, Colour rightButtonDisabledColor, Colour leftButtonDisabledColor) {
217
-        List<Button> rightButtons = mergeButtonsWithColor(buttons.right, rightButtonColor, rightButtonDisabledColor);
218
-        List<Button> leftButtons = mergeButtonsWithColor(buttons.left, leftButtonColor, leftButtonDisabledColor);
219
-        if (buttons.right != null) topBar.setRightButtons(rightButtons);
220
-        if (buttons.left != null) topBar.setLeftButtons(leftButtons);
221
-        if (buttons.back.hasValue()) topBar.setBackButton(buttons.back);
297
+    private void mergeButtons(TopBarOptions options, TopBarButtons buttons, Component child) {
298
+        List<Button> rightButtons = mergeButtonsWithColor(buttons.right, options.rightButtonColor, options.rightButtonDisabledColor);
299
+        List<Button> leftButtons = mergeButtonsWithColor(buttons.left, options.leftButtonColor, options.leftButtonDisabledColor);
300
+
301
+        List<TitleBarButtonController> rightButtonControllers = getOrCreateButtonControllers(componentRightButtons.get(child), rightButtons);
302
+        List<TitleBarButtonController> leftButtonControllers = getOrCreateButtonControllers(componentLeftButtons.get(child), leftButtons);
303
+
304
+        if (rightButtonControllers != null) {
305
+            Map previousRightButtons = componentRightButtons.put(child, keyBy(rightButtonControllers, TitleBarButtonController::getButtonInstanceId));
306
+            if (previousRightButtons != null) forEach(previousRightButtons.values(), TitleBarButtonController::destroy);
307
+        }
308
+        if (leftButtonControllers != null) {
309
+            Map previousLeftButtons = componentLeftButtons.put(child, keyBy(leftButtonControllers, TitleBarButtonController::getButtonInstanceId));
310
+            if (previousLeftButtons != null) forEach(previousLeftButtons.values(), TitleBarButtonController::destroy);
311
+        }
312
+
313
+        if (buttons.right != null) topBar.setRightButtons(rightButtonControllers);
314
+        if (buttons.left != null) topBar.setLeftButtons(leftButtonControllers);
315
+        if (buttons.back.hasValue()) topBar.setBackButton(createButtonController(buttons.back));
222 316
     }
223 317
 
224 318
     @Nullable
@@ -308,7 +402,7 @@ public class StackOptionsPresenter {
308 402
     }
309 403
 
310 404
     private LayoutParams getComponentLayoutParams(com.reactnativenavigation.parse.Component component) {
311
-        return new Toolbar.LayoutParams(component.alignment == Alignment.Center ? Gravity.CENTER : Gravity.START);
405
+        return new Toolbar.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, component.alignment == Alignment.Center ? Gravity.CENTER : Gravity.START);
312 406
     }
313 407
 
314 408
     @RestrictTo(RestrictTo.Scope.TESTS)

+ 45
- 7
lib/android/app/src/main/java/com/reactnativenavigation/utils/CollectionUtils.java View File

@@ -1,28 +1,49 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3
+import android.support.annotation.NonNull;
4
+import android.support.annotation.Nullable;
5
+
3 6
 import java.util.ArrayList;
4 7
 import java.util.Collection;
8
+import java.util.Collections;
5 9
 import java.util.HashMap;
6 10
 import java.util.List;
7 11
 import java.util.Map;
8 12
 
9 13
 public class CollectionUtils {
14
+    public interface Apply<T> {
15
+        void on(T t);
16
+    }
17
+
10 18
     public static boolean isNullOrEmpty(Collection collection) {
11 19
         return collection == null || collection.isEmpty();
12 20
     }
13 21
 
14
-    public interface Mapper<K, V> {
15
-        K map(V value);
22
+    public interface KeyBy<K, V> {
23
+        K by(V value);
16 24
     }
17 25
 
18
-    public static <K, V> Map<K, V> map(Collection<V> elements, Mapper<K, V> mapper) {
26
+    public static <K, V> Map<K, V> keyBy(Collection<V> elements, KeyBy<K, V> key) {
19 27
         Map<K, V> map = new HashMap<>();
20 28
         for (V value : elements) {
21
-            map.put(mapper.map(value), value);
29
+            map.put(key.by(value), value);
22 30
         }
23 31
         return map;
24 32
     }
25 33
 
34
+    public interface Mapper<T, S> {
35
+        S map(T value);
36
+    }
37
+
38
+    public static @Nullable <T, S> List<S> map(@Nullable Collection<T> items, Mapper<T, S> map) {
39
+        if (items == null) return null;
40
+        List<S> result = new ArrayList<>();
41
+        for (T item : items) {
42
+            result.add(map.map(item));
43
+        }
44
+        return result;
45
+    }
46
+
26 47
     public interface Filter<T> {
27 48
         boolean filter(T value);
28 49
     }
@@ -35,9 +56,26 @@ public class CollectionUtils {
35 56
         return result;
36 57
     }
37 58
 
38
-    public static <T> List<T> merge(Collection<T> a, Collection<T> b) {
39
-        List<T> result = new ArrayList<>(a);
40
-        result.addAll(b);
59
+    public static <T> List<T> merge(@Nullable Collection<T> a, @Nullable Collection<T> b, @NonNull List<T> defaultValue) {
60
+        List<T> result = merge(a, b);
61
+        return result == null ? defaultValue : result;
62
+    }
63
+
64
+    public static <T> List<T> merge(@Nullable Collection<T> a, @Nullable Collection<T> b) {
65
+        if (a == null && b == null) return null;
66
+        List<T> result = new ArrayList<>(get(a));
67
+        result.addAll(get(b));
41 68
         return result;
42 69
     }
70
+
71
+    public static <T> void forEach(@Nullable Collection<T> items, Apply<T> apply) {
72
+        if (items == null) return;
73
+        for (T item : items) {
74
+            apply.on(item);
75
+        }
76
+    }
77
+
78
+    private static @NonNull <T> Collection<T> get(@Nullable Collection<T> t) {
79
+        return t == null ? Collections.EMPTY_LIST : t;
80
+    }
43 81
 }

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

@@ -6,6 +6,7 @@ import android.view.ViewGroup;
6 6
 
7 7
 import com.reactnativenavigation.parse.Options;
8 8
 import com.reactnativenavigation.presentation.OptionsPresenter;
9
+import com.reactnativenavigation.views.Component;
9 10
 
10 11
 public abstract class ChildController<T extends ViewGroup> extends ViewController<T>  {
11 12
     final OptionsPresenter presenter;
@@ -52,6 +53,14 @@ public abstract class ChildController<T extends ViewGroup> extends ViewControlle
52 53
         }
53 54
     }
54 55
 
56
+    @Override
57
+    public void destroy() {
58
+        if (!isDestroyed() && getView() instanceof Component) {
59
+            performOnParentController(parent -> parent.onChildDestroyed((Component) getView()));
60
+        }
61
+        super.destroy();
62
+    }
63
+
55 64
     protected boolean isRoot() {
56 65
         return getParentController() == null &&
57 66
                 !(this instanceof Navigator) &&

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

@@ -67,12 +67,6 @@ public class ComponentViewController extends ChildController<ComponentLayout> {
67 67
         super.mergeOptions(options);
68 68
     }
69 69
 
70
-    @Override
71
-    public void destroy() {
72
-        if (!isDestroyed()) performOnParentController(parent -> parent.onChildDestroyed(getView()));
73
-        super.destroy();
74
-    }
75
-
76 70
     ReactComponent getComponent() {
77 71
         return view;
78 72
     }

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TopBarButtonController.java → lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarButtonController.java View File

@@ -5,6 +5,7 @@ import android.app.Activity;
5 5
 import android.graphics.Color;
6 6
 import android.graphics.drawable.Drawable;
7 7
 import android.support.annotation.NonNull;
8
+import android.support.annotation.RestrictTo;
8 9
 import android.support.v7.widget.ActionMenuView;
9 10
 import android.support.v7.widget.Toolbar;
10 11
 import android.view.MenuItem;
@@ -28,7 +29,7 @@ import com.reactnativenavigation.views.titlebar.TitleBarReactButtonView;
28 29
 import java.util.Collections;
29 30
 import java.util.List;
30 31
 
31
-public class TopBarButtonController extends ViewController<TitleBarReactButtonView> implements MenuItem.OnMenuItemClickListener {
32
+public class TitleBarButtonController extends ViewController<TitleBarReactButtonView> implements MenuItem.OnMenuItemClickListener {
32 33
     public interface OnClickListener {
33 34
         void onPress(String buttonId);
34 35
     }
@@ -38,16 +39,25 @@ public class TopBarButtonController extends ViewController<TitleBarReactButtonVi
38 39
     private ButtonOptionsPresenter optionsPresenter;
39 40
     private final Button button;
40 41
     private final ReactViewCreator viewCreator;
41
-    private TopBarButtonController.OnClickListener onPressListener;
42
+    private TitleBarButtonController.OnClickListener onPressListener;
42 43
     private Drawable icon;
43 44
 
44
-    public TopBarButtonController(Activity activity,
45
-                                  NavigationIconResolver navigationIconResolver,
46
-                                  ImageLoader imageLoader,
47
-                                  ButtonOptionsPresenter optionsPresenter,
48
-                                  Button button,
49
-                                  ReactViewCreator viewCreator,
50
-                                  OnClickListener onClickListener) {
45
+    @RestrictTo(RestrictTo.Scope.TESTS)
46
+    public Button getButton() {
47
+        return button;
48
+    }
49
+
50
+    public String getButtonInstanceId() {
51
+        return button.instanceId;
52
+    }
53
+
54
+    public TitleBarButtonController(Activity activity,
55
+                                    NavigationIconResolver navigationIconResolver,
56
+                                    ImageLoader imageLoader,
57
+                                    ButtonOptionsPresenter optionsPresenter,
58
+                                    Button button,
59
+                                    ReactViewCreator viewCreator,
60
+                                    OnClickListener onClickListener) {
51 61
         super(activity, button.id, new Options());
52 62
         this.navigationIconResolver = navigationIconResolver;
53 63
         this.imageLoader = imageLoader;
@@ -124,7 +134,7 @@ public class TopBarButtonController extends ViewController<TitleBarReactButtonVi
124 134
                     @Override
125 135
                     public void onComplete(@NonNull List<Drawable> icons) {
126 136
                         Drawable icon = icons.get(0);
127
-                        TopBarButtonController.this.icon = icon;
137
+                        TitleBarButtonController.this.icon = icon;
128 138
                         setIconColor(icon);
129 139
                         menuItem.setIcon(icon);
130 140
                     }

+ 1
- 2
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java View File

@@ -52,6 +52,7 @@ public class StackController extends ParentController<StackLayout> {
52 52
         this.animator = animator;
53 53
         this.backButtonHelper = backButtonHelper;
54 54
         this.presenter = stackPresenter;
55
+        stackPresenter.setButtonOnClickListener(this::onNavigationButtonPressed);
55 56
         for (ViewController child : children) {
56 57
             stack.push(child.getId(), child);
57 58
             child.setParentController(this);
@@ -298,10 +299,8 @@ public class StackController extends ParentController<StackLayout> {
298 299
     @Override
299 300
     protected StackLayout createView() {
300 301
         StackLayout stackLayout = new StackLayout(getActivity(),
301
-                topBarButtonCreator,
302 302
                 topBarBackgroundViewController,
303 303
                 topBarController,
304
-                this::onNavigationButtonPressed,
305 304
                 getId()
306 305
         );
307 306
         presenter.bindView(topBarController.getView());

+ 4
- 7
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarController.java View File

@@ -5,9 +5,6 @@ import android.support.v4.view.ViewPager;
5 5
 import android.view.View;
6 6
 
7 7
 import com.reactnativenavigation.utils.CompatUtils;
8
-import com.reactnativenavigation.utils.ImageLoader;
9
-import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
10
-import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
11 8
 import com.reactnativenavigation.views.StackLayout;
12 9
 import com.reactnativenavigation.views.topbar.TopBar;
13 10
 
@@ -15,16 +12,16 @@ import com.reactnativenavigation.views.topbar.TopBar;
15 12
 public class TopBarController {
16 13
     private TopBar topBar;
17 14
 
18
-    public View createView(Context context, ReactViewCreator buttonCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout) {
15
+    public View createView(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
19 16
         if (topBar == null) {
20
-            topBar = createTopBar(context, buttonCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout, new ImageLoader());
17
+            topBar = createTopBar(context, topBarBackgroundViewController, stackLayout);
21 18
             topBar.setId(CompatUtils.generateViewId());
22 19
         }
23 20
         return topBar;
24 21
     }
25 22
 
26
-    protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout, ImageLoader imageLoader) {
27
-        return new TopBar(context, buttonCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout, imageLoader);
23
+    protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
24
+        return new TopBar(context, topBarBackgroundViewController, stackLayout);
28 25
     }
29 26
 
30 27
     public void clear() {

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

@@ -12,7 +12,7 @@ import com.reactnativenavigation.parse.Options;
12 12
 import com.reactnativenavigation.utils.UiUtils;
13 13
 import com.reactnativenavigation.utils.ViewUtils;
14 14
 import com.reactnativenavigation.viewcontrollers.IReactView;
15
-import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
15
+import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
16 16
 import com.reactnativenavigation.views.element.Element;
17 17
 import com.reactnativenavigation.views.topbar.TopBar;
18 18
 import com.reactnativenavigation.views.touch.OverlayTouchDelegate;
@@ -22,7 +22,7 @@ import java.util.List;
22 22
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
23 23
 
24 24
 @SuppressLint("ViewConstructor")
25
-public class ComponentLayout extends FrameLayout implements ReactComponent, TopBarButtonController.OnClickListener {
25
+public class ComponentLayout extends FrameLayout implements ReactComponent, TitleBarButtonController.OnClickListener {
26 26
 
27 27
     private IReactView reactView;
28 28
     private final OverlayTouchDelegate touchDelegate;

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

@@ -4,28 +4,23 @@ import android.annotation.SuppressLint;
4 4
 import android.content.Context;
5 5
 import android.widget.RelativeLayout;
6 6
 
7
-import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
8
-import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
9 7
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
10 8
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
11 9
 import com.reactnativenavigation.views.topbar.TopBar;
12 10
 
13
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
14
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
15
-
16 11
 @SuppressLint("ViewConstructor")
17 12
 public class StackLayout extends RelativeLayout implements Component {
18 13
     private String stackId;
19 14
 
20
-    public StackLayout(Context context, ReactViewCreator topBarButtonCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, TopBarButtonController.OnClickListener topBarButtonClickListener, String stackId) {
15
+    public StackLayout(Context context, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, String stackId) {
21 16
         super(context);
22 17
         this.stackId = stackId;
23
-        createLayout(topBarButtonCreator, topBarBackgroundViewController, topBarController, topBarButtonClickListener);
18
+        createLayout(topBarBackgroundViewController, topBarController);
24 19
         setContentDescription("StackLayout");
25 20
     }
26 21
 
27
-    private void createLayout(ReactViewCreator buttonCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, TopBarButtonController.OnClickListener topBarButtonClickListener) {
28
-        addView(topBarController.createView(getContext(), buttonCreator, topBarBackgroundViewController, topBarButtonClickListener, this), MATCH_PARENT, WRAP_CONTENT);
22
+    private void createLayout(TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController) {
23
+        addView(topBarController.createView(getContext(), topBarBackgroundViewController, this));
29 24
     }
30 25
 
31 26
     public String getStackId() {

+ 3
- 4
lib/android/app/src/main/java/com/reactnativenavigation/views/element/ElementTransitionManager.java View File

@@ -2,7 +2,6 @@ package com.reactnativenavigation.views.element;
2 2
 
3 3
 import android.animation.Animator;
4 4
 import android.support.annotation.RestrictTo;
5
-import android.util.Log;
6 5
 
7 6
 import com.reactnativenavigation.parse.Transition;
8 7
 import com.reactnativenavigation.parse.Transitions;
@@ -13,7 +12,7 @@ import java.util.List;
13 12
 import java.util.Map;
14 13
 
15 14
 import static com.reactnativenavigation.utils.CollectionUtils.filter;
16
-import static com.reactnativenavigation.utils.CollectionUtils.map;
15
+import static com.reactnativenavigation.utils.CollectionUtils.keyBy;
17 16
 
18 17
 public class ElementTransitionManager {
19 18
 
@@ -33,8 +32,8 @@ public class ElementTransitionManager {
33 32
 
34 33
     public Collection<Animator> createTransitions(Transitions transitions, List<Element> fromElements, List<Element> toElements) {
35 34
         if (!transitions.hasValue() || fromElements.isEmpty() || toElements.isEmpty()) return Collections.EMPTY_LIST;
36
-        Map<String, Element> from = map(fromElements, Element::getElementId);
37
-        Map<String, Element> to = map(toElements, Element::getElementId);
35
+        Map<String, Element> from = keyBy(fromElements, Element::getElementId);
36
+        Map<String, Element> to = keyBy(toElements, Element::getElementId);
38 37
         List<Transition> validTransitions = filter(transitions.get(), (t) -> validator.validate(t, from, to));
39 38
 
40 39
         return animatorCreator.create(validTransitions, from, to);

+ 10
- 43
lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBar.java View File

@@ -1,7 +1,6 @@
1 1
 package com.reactnativenavigation.views.titlebar;
2 2
 
3 3
 import android.annotation.SuppressLint;
4
-import android.app.Activity;
5 4
 import android.content.Context;
6 5
 import android.graphics.Typeface;
7 6
 import android.support.v7.widget.Toolbar;
@@ -11,18 +10,11 @@ import android.view.ViewGroup;
11 10
 import android.widget.TextView;
12 11
 
13 12
 import com.reactnativenavigation.parse.Alignment;
14
-import com.reactnativenavigation.parse.BackButton;
15
-import com.reactnativenavigation.parse.params.Button;
16 13
 import com.reactnativenavigation.parse.params.Colour;
17
-import com.reactnativenavigation.utils.ButtonOptionsPresenter;
18
-import com.reactnativenavigation.utils.ImageLoader;
19 14
 import com.reactnativenavigation.utils.UiUtils;
20 15
 import com.reactnativenavigation.utils.ViewUtils;
21
-import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
22
-import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
23
-import com.reactnativenavigation.viewcontrollers.button.NavigationIconResolver;
16
+import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
24 17
 
25
-import java.util.ArrayList;
26 18
 import java.util.List;
27 19
 
28 20
 import javax.annotation.Nullable;
@@ -31,18 +23,11 @@ import javax.annotation.Nullable;
31 23
 public class TitleBar extends Toolbar {
32 24
     public static final int DEFAULT_LEFT_MARGIN = 16;
33 25
 
34
-    private final ReactViewCreator buttonCreator;
35
-    private final TopBarButtonController.OnClickListener onClickListener;
36
-    private final List<TopBarButtonController> rightButtonControllers = new ArrayList<>();
37
-    private TopBarButtonController leftButtonController;
38
-    private ImageLoader imageLoader;
26
+    private TitleBarButtonController leftButtonController;
39 27
     private View component;
40 28
 
41
-    public TitleBar(Context context, ReactViewCreator buttonCreator, TopBarButtonController.OnClickListener onClickListener, ImageLoader imageLoader) {
29
+    public TitleBar(Context context) {
42 30
         super(context);
43
-        this.buttonCreator = buttonCreator;
44
-        this.imageLoader = imageLoader;
45
-        this.onClickListener = onClickListener;
46 31
         getMenu();
47 32
         setContentDescription("titleBar");
48 33
     }
@@ -160,18 +145,14 @@ public class TitleBar extends Toolbar {
160 145
     }
161 146
 
162 147
     private void clearRightButtons() {
163
-        for (TopBarButtonController button : rightButtonControllers) {
164
-            button.destroy();
165
-        }
166
-        rightButtonControllers.clear();
167 148
         if (getMenu().size() > 0) getMenu().clear();
168 149
     }
169 150
 
170
-    public void setBackButton(BackButton button) {
151
+    public void setBackButton(TitleBarButtonController button) {
171 152
         setLeftButton(button);
172 153
     }
173 154
 
174
-    public void setLeftButtons(List<Button> leftButtons) {
155
+    public void setLeftButtons(List<TitleBarButtonController> leftButtons) {
175 156
         if (leftButtons == null) return;
176 157
         if (leftButtons.isEmpty()) {
177 158
             clearLeftButton();
@@ -183,33 +164,19 @@ public class TitleBar extends Toolbar {
183 164
         setLeftButton(leftButtons.get(0));
184 165
     }
185 166
 
186
-    private void setLeftButton(final Button button) {
187
-        TopBarButtonController controller = createButtonController(button);
188
-        leftButtonController = controller;
189
-        controller.applyNavigationIcon(this);
167
+    private void setLeftButton(TitleBarButtonController button) {
168
+        leftButtonController = button;
169
+        button.applyNavigationIcon(this);
190 170
     }
191 171
 
192
-    public void setRightButtons(List<Button> rightButtons) {
172
+    public void setRightButtons(List<TitleBarButtonController> rightButtons) {
193 173
         if (rightButtons == null) return;
194 174
         clearRightButtons();
195 175
         for (int i = 0; i < rightButtons.size(); i++) {
196
-            TopBarButtonController controller = createButtonController(rightButtons.get(i));
197
-            rightButtonControllers.add(controller);
198
-            controller.addToMenu(this, rightButtons.size() - i - 1);
176
+            rightButtons.get(i).addToMenu(this, rightButtons.size() - i - 1);
199 177
         }
200 178
     }
201 179
 
202
-    public TopBarButtonController createButtonController(Button button) {
203
-        return new TopBarButtonController((Activity) getContext(),
204
-                new NavigationIconResolver(getContext(), imageLoader),
205
-                imageLoader,
206
-                new ButtonOptionsPresenter(this, button),
207
-                button,
208
-                buttonCreator,
209
-                onClickListener
210
-        );
211
-    }
212
-
213 180
     public void setHeight(int height) {
214 181
         if (height == getLayoutParams().height) return;
215 182
         ViewGroup.LayoutParams lp = getLayoutParams();

+ 11
- 17
lib/android/app/src/main/java/com/reactnativenavigation/views/topbar/TopBar.java View File

@@ -26,16 +26,12 @@ import com.reactnativenavigation.anim.TopBarCollapseBehavior;
26 26
 import com.reactnativenavigation.interfaces.ScrollEventListener;
27 27
 import com.reactnativenavigation.parse.Alignment;
28 28
 import com.reactnativenavigation.parse.AnimationOptions;
29
-import com.reactnativenavigation.parse.BackButton;
30 29
 import com.reactnativenavigation.parse.Component;
31
-import com.reactnativenavigation.parse.params.Button;
32 30
 import com.reactnativenavigation.parse.params.Colour;
33 31
 import com.reactnativenavigation.parse.params.Number;
34 32
 import com.reactnativenavigation.utils.CompatUtils;
35
-import com.reactnativenavigation.utils.ImageLoader;
36 33
 import com.reactnativenavigation.utils.UiUtils;
37
-import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
38
-import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
34
+import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
39 35
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
40 36
 import com.reactnativenavigation.views.StackLayout;
41 37
 import com.reactnativenavigation.views.titlebar.TitleBar;
@@ -55,29 +51,27 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
55 51
     private FrameLayout root;
56 52
     private TopBarBackgroundViewController topBarBackgroundViewController;
57 53
     private View border;
58
-    private ImageLoader imageLoader;
59 54
 
60
-    public TopBar(final Context context, ReactViewCreator buttonCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener onClickListener, StackLayout parentView, ImageLoader imageLoader) {
55
+    public TopBar(final Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout parentView) {
61 56
         super(context);
62 57
         context.setTheme(R.style.TopBar);
63
-        this.imageLoader = imageLoader;
64 58
         collapsingBehavior = new TopBarCollapseBehavior(this);
65 59
         this.topBarBackgroundViewController = topBarBackgroundViewController;
66 60
         topTabs = new TopTabs(getContext());
67 61
         animator = new TopBarAnimator(this, parentView.getStackId());
68
-        createLayout(buttonCreator, onClickListener);
62
+        createLayout();
69 63
     }
70 64
 
71
-    private void createLayout(ReactViewCreator buttonCreator, TopBarButtonController.OnClickListener onClickListener) {
65
+    private void createLayout() {
72 66
         setId(CompatUtils.generateViewId());
73
-        titleBar = createTitleBar(getContext(), buttonCreator, onClickListener, imageLoader);
67
+        titleBar = createTitleBar(getContext());
74 68
         topTabs = createTopTabs();
75 69
         border = createBorder();
76 70
         LinearLayout content = createContentLayout();
77 71
 
78 72
         root = new FrameLayout(getContext());
79 73
         root.setId(CompatUtils.generateViewId());
80
-        content.addView(titleBar);
74
+        content.addView(titleBar, MATCH_PARENT, WRAP_CONTENT);
81 75
         content.addView(topTabs);
82 76
         root.addView(content);
83 77
         root.addView(border);
@@ -110,8 +104,8 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
110 104
         return border;
111 105
     }
112 106
 
113
-    protected TitleBar createTitleBar(Context context, ReactViewCreator buttonCreator, TopBarButtonController.OnClickListener onClickListener, ImageLoader imageLoader) {
114
-        TitleBar titleBar = new TitleBar(context, buttonCreator, onClickListener, imageLoader);
107
+    protected TitleBar createTitleBar(Context context) {
108
+        TitleBar titleBar = new TitleBar(context);
115 109
         titleBar.setId(CompatUtils.generateViewId());
116 110
         return titleBar;
117 111
     }
@@ -209,15 +203,15 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
209 203
         topTabs.setLayoutParams(topTabs.getLayoutParams());
210 204
     }
211 205
 
212
-    public void setBackButton(BackButton backButton) {
206
+    public void setBackButton(TitleBarButtonController backButton) {
213 207
         titleBar.setBackButton(backButton);
214 208
     }
215 209
 
216
-    public void setLeftButtons(List<Button> leftButtons) {
210
+    public void setLeftButtons(List<TitleBarButtonController> leftButtons) {
217 211
         titleBar.setLeftButtons(leftButtons);
218 212
     }
219 213
 
220
-    public void setRightButtons(List<Button> rightButtons) {
214
+    public void setRightButtons(List<TitleBarButtonController> rightButtons) {
221 215
         titleBar.setRightButtons(rightButtons);
222 216
     }
223 217
 

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

@@ -9,7 +9,7 @@ import android.widget.RelativeLayout;
9 9
 
10 10
 import com.reactnativenavigation.parse.Options;
11 11
 import com.reactnativenavigation.viewcontrollers.IReactView;
12
-import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
12
+import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
13 13
 import com.reactnativenavigation.viewcontrollers.ViewController;
14 14
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsAdapter;
15 15
 import com.reactnativenavigation.views.Component;
@@ -21,7 +21,7 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
21 21
 import static android.widget.RelativeLayout.BELOW;
22 22
 
23 23
 @SuppressLint("ViewConstructor")
24
-public class TopTabsViewPager extends ViewPager implements Component, TopBarButtonController.OnClickListener {
24
+public class TopTabsViewPager extends ViewPager implements Component, TitleBarButtonController.OnClickListener {
25 25
 
26 26
     private static final int OFFSCREEN_PAGE_LIMIT = 99;
27 27
     private List<ViewController> tabs;

+ 3
- 5
lib/android/app/src/test/java/com/reactnativenavigation/TestUtils.java View File

@@ -10,8 +10,6 @@ import com.reactnativenavigation.parse.Options;
10 10
 import com.reactnativenavigation.presentation.StackOptionsPresenter;
11 11
 import com.reactnativenavigation.utils.ImageLoader;
12 12
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
13
-import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
14
-import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
15 13
 import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
16 14
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
17 15
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
@@ -27,13 +25,13 @@ public class TestUtils {
27 25
                 .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
28 26
                 .setTopBarController(new TopBarController() {
29 27
                     @Override
30
-                    protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout, ImageLoader imageLoader) {
31
-                        TopBar topBar = super.createTopBar(context, buttonCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout, imageLoader);
28
+                    protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
29
+                        TopBar topBar = super.createTopBar(context, topBarBackgroundViewController, stackLayout);
32 30
                         topBar.layout(0, 0, 1000, 100);
33 31
                         return topBar;
34 32
                     }
35 33
                 })
36
-                .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new Options()))
34
+                .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new Options())                )
37 35
                 .setInitialOptions(new Options());
38 36
     }
39 37
 }

+ 2
- 2
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestComponentLayout.java View File

@@ -7,10 +7,10 @@ import android.view.View;
7 7
 import com.reactnativenavigation.interfaces.ScrollEventListener;
8 8
 import com.reactnativenavigation.parse.Options;
9 9
 import com.reactnativenavigation.viewcontrollers.IReactView;
10
-import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
10
+import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
11 11
 import com.reactnativenavigation.views.ComponentLayout;
12 12
 
13
-public class TestComponentLayout extends ComponentLayout implements TopBarButtonController.OnClickListener {
13
+public class TestComponentLayout extends ComponentLayout implements TitleBarButtonController.OnClickListener {
14 14
 
15 15
     private IReactView reactView;
16 16
 

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

@@ -1,10 +1,55 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3
+import android.app.Activity;
3 4
 import android.support.v7.view.menu.ActionMenuItemView;
4 5
 import android.support.v7.widget.Toolbar;
5 6
 
7
+import com.reactnativenavigation.mocks.ImageLoaderMock;
8
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
9
+import com.reactnativenavigation.parse.Component;
10
+import com.reactnativenavigation.parse.params.Button;
11
+import com.reactnativenavigation.parse.params.Text;
12
+import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
13
+import com.reactnativenavigation.viewcontrollers.button.NavigationIconResolver;
14
+import com.reactnativenavigation.views.titlebar.TitleBar;
15
+
6 16
 public class TitleBarHelper {
7 17
     public static ActionMenuItemView getRightButton(Toolbar toolbar, int index) {
8 18
         return (ActionMenuItemView) ViewUtils.findChildrenByClassRecursive(toolbar, ActionMenuItemView.class).get(toolbar.getMenu().size() - index - 1);
9 19
     }
20
+
21
+    public static Button textualButton(String text) {
22
+        Button button = new Button();
23
+        button.id = text + CompatUtils.generateViewId();
24
+        button.text = new Text(text);
25
+        return button;
26
+    }
27
+
28
+    public static Button reactViewButton(String name) {
29
+        Button button = new Button();
30
+        button.id = name + CompatUtils.generateViewId();
31
+        button.component = new Component();
32
+        button.component.name = new Text("com.example" + name + CompatUtils.generateViewId());
33
+        button.component.componentId = new Text(name + CompatUtils.generateViewId());
34
+        return button;
35
+    }
36
+
37
+    public static Button iconButton(String id, String icon) {
38
+        Button button = new Button();
39
+        button.id = "someButton";
40
+        button.icon = new Text(icon);
41
+        return button;
42
+    }
43
+
44
+
45
+    public static TitleBarButtonController createButtonController(Activity activity, TitleBar titleBar, Button button) {
46
+        return new TitleBarButtonController(activity,
47
+                new NavigationIconResolver(activity, ImageLoaderMock.mock()),
48
+                ImageLoaderMock.mock(),
49
+                new ButtonOptionsPresenter(titleBar, button),
50
+                button,
51
+                new TopBarButtonCreatorMock(),
52
+                buttonId -> {}
53
+        );
54
+    }
10 55
 }

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

@@ -12,6 +12,7 @@ import com.reactnativenavigation.TestUtils;
12 12
 import com.reactnativenavigation.mocks.ImageLoaderMock;
13 13
 import com.reactnativenavigation.mocks.SimpleViewController;
14 14
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
15
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
15 16
 import com.reactnativenavigation.parse.Options;
16 17
 import com.reactnativenavigation.parse.params.Bool;
17 18
 import com.reactnativenavigation.parse.params.Colour;
@@ -284,7 +285,7 @@ public class BottomTabsControllerTest extends BaseTest {
284 285
         return TestUtils.newStackController(activity)
285 286
                 .setId(id)
286 287
                 .setInitialOptions(tabOptions)
287
-                .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new Options()))
288
+                .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new Options()))
288 289
                 .build();
289 290
     }
290 291
 

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

@@ -69,9 +69,8 @@ public class OptionsApplyingTest extends BaseTest {
69 69
         );
70 70
         TopBarController topBarController = new TopBarController() {
71 71
             @Override
72
-            protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout, ImageLoader imageLoader) {
73
-                topBar =
74
-                        spy(super.createTopBar(context, buttonCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout, imageLoader));
72
+            protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
73
+                topBar = spy(super.createTopBar(context, topBarBackgroundViewController, stackLayout));
75 74
                 return topBar;
76 75
             }
77 76
         };
@@ -104,7 +103,7 @@ public class OptionsApplyingTest extends BaseTest {
104 103
                         .setTopBarController(new TopBarController())
105 104
                         .setId("stackId")
106 105
                         .setInitialOptions(new Options())
107
-                        .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new Options()))
106
+                        .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new Options()))
108 107
                         .build();
109 108
         stackController.ensureViewIsCreated();
110 109
         stackController.push(uut, new CommandListenerAdapter());

+ 160
- 18
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackOptionsPresenterTest.java View File

@@ -3,13 +3,16 @@ package com.reactnativenavigation.viewcontrollers;
3 3
 import android.app.Activity;
4 4
 import android.graphics.Typeface;
5 5
 import android.support.v7.widget.Toolbar;
6
+import android.util.Log;
6 7
 import android.view.Gravity;
7 8
 import android.view.View;
8 9
 
9 10
 import com.reactnativenavigation.BaseTest;
11
+import com.reactnativenavigation.mocks.ImageLoaderMock;
10 12
 import com.reactnativenavigation.mocks.TestComponentLayout;
11 13
 import com.reactnativenavigation.mocks.TestReactView;
12 14
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
15
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
13 16
 import com.reactnativenavigation.parse.Alignment;
14 17
 import com.reactnativenavigation.parse.Component;
15 18
 import com.reactnativenavigation.parse.Options;
@@ -23,6 +26,7 @@ import com.reactnativenavigation.parse.params.Fraction;
23 26
 import com.reactnativenavigation.parse.params.Number;
24 27
 import com.reactnativenavigation.parse.params.Text;
25 28
 import com.reactnativenavigation.presentation.StackOptionsPresenter;
29
+import com.reactnativenavigation.utils.TitleBarHelper;
26 30
 import com.reactnativenavigation.views.titlebar.TitleBarReactView;
27 31
 import com.reactnativenavigation.views.topbar.TopBar;
28 32
 
@@ -32,8 +36,10 @@ import org.mockito.ArgumentCaptor;
32 36
 import org.mockito.Mockito;
33 37
 
34 38
 import java.util.ArrayList;
39
+import java.util.Collections;
35 40
 import java.util.List;
36 41
 
42
+import static com.reactnativenavigation.utils.CollectionUtils.forEach;
37 43
 import static org.assertj.core.api.Java6Assertions.assertThat;
38 44
 import static org.mockito.ArgumentMatchers.any;
39 45
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -54,9 +60,21 @@ public class StackOptionsPresenterTest extends BaseTest {
54 60
     private Activity activity;
55 61
     private TopBar topBar;
56 62
 
63
+    private Button textBtn1 = TitleBarHelper.textualButton("btn1");
64
+    private Button textBtn2 = TitleBarHelper.textualButton("btn2");
65
+    private Button componentBtn1 = TitleBarHelper.reactViewButton("btn1_");
66
+    private Button componentBtn2 = TitleBarHelper.reactViewButton("btn2_");
67
+
57 68
     @Override
58 69
     public void beforeEach() {
59 70
         activity = spy(newActivity());
71
+        //noinspection Convert2Lambda
72
+        TitleBarButtonController.OnClickListener onClickListener = spy(new TitleBarButtonController.OnClickListener() {
73
+            @Override
74
+            public void onPress(String buttonId) {
75
+                Log.i("TopBarTest", "onPress: " + buttonId);
76
+            }
77
+        });
60 78
 
61 79
         TitleBarReactViewCreatorMock titleViewCreator = new TitleBarReactViewCreatorMock() {
62 80
             @Override
@@ -64,9 +82,10 @@ public class StackOptionsPresenterTest extends BaseTest {
64 82
                 return spy(super.create(activity, componentId, componentName));
65 83
             }
66 84
         };
67
-        uut = spy(new StackOptionsPresenter(activity, titleViewCreator, new Options()));
85
+        uut = spy(new StackOptionsPresenter(activity, titleViewCreator, new TopBarButtonCreatorMock(), ImageLoaderMock.mock(), new Options()));
68 86
         topBar = mockTopBar();
69 87
         uut.bindView(topBar);
88
+        uut.setButtonOnClickListener(onClickListener);
70 89
         child = spy(new TestComponentLayout(activity, new TestReactView(activity)));
71 90
         otherChild = new TestComponentLayout(activity, new TestReactView(activity));
72 91
     }
@@ -130,11 +149,11 @@ public class StackOptionsPresenterTest extends BaseTest {
130 149
 
131 150
     @Test
132 151
     public void mergeButtons() {
133
-        Options options = new Options();
134
-        uut.mergeChildOptions(options, EMPTY_OPTIONS, child);
152
+        uut.mergeChildOptions(EMPTY_OPTIONS, EMPTY_OPTIONS, child);
135 153
         verify(topBar, times(0)).setRightButtons(any());
136 154
         verify(topBar, times(0)).setLeftButtons(any());
137 155
 
156
+        Options options = new Options();
138 157
         options.topBar.buttons.right = new ArrayList<>();
139 158
         uut.mergeChildOptions(options, EMPTY_OPTIONS, child);
140 159
         verify(topBar, times(1)).setRightButtons(any());
@@ -144,6 +163,53 @@ public class StackOptionsPresenterTest extends BaseTest {
144 163
         verify(topBar, times(1)).setLeftButtons(any());
145 164
     }
146 165
 
166
+    @Test
167
+    public void mergeButtons_previousRightButtonsAreDestroyed() {
168
+        Options options = new Options();
169
+        options.topBar.buttons.right = new ArrayList<>(Collections.singletonList(componentBtn1));
170
+        uut.applyChildOptions(options, child);
171
+        List<TitleBarButtonController> initialButtons = uut.getComponentButtons(child);
172
+        forEach(initialButtons, ViewController::ensureViewIsCreated);
173
+
174
+        options.topBar.buttons.right = new ArrayList<>(Collections.singletonList(componentBtn2));
175
+        uut.mergeChildOptions(options, new Options(), child);
176
+        for (TitleBarButtonController button : initialButtons) {
177
+            assertThat(button.isDestroyed()).isTrue();
178
+        }
179
+    }
180
+
181
+    @Test
182
+    public void mergeButtons_mergingRightButtonsOnlyDestroysRightButtons() {
183
+        Options a = new Options();
184
+        a.topBar.buttons.right = new ArrayList<>(Collections.singletonList(componentBtn1));
185
+        a.topBar.buttons.left = new ArrayList<>(Collections.singletonList(componentBtn2));
186
+        uut.applyChildOptions(a, child);
187
+        List<TitleBarButtonController> initialButtons = uut.getComponentButtons(child);
188
+        forEach(initialButtons, ViewController::ensureViewIsCreated);
189
+
190
+        Options b = new Options();
191
+        b.topBar.buttons.right = new ArrayList<>(Collections.singletonList(componentBtn2));
192
+        uut.mergeChildOptions(b, new Options(), child);
193
+        assertThat(initialButtons.get(0).isDestroyed()).isTrue();
194
+        assertThat(initialButtons.get(1).isDestroyed()).isFalse();
195
+    }
196
+
197
+    @Test
198
+    public void mergeButtons_mergingLeftButtonsOnlyDestroysLeftButtons() {
199
+        Options a = new Options();
200
+        a.topBar.buttons.right = new ArrayList<>(Collections.singletonList(componentBtn1));
201
+        a.topBar.buttons.left = new ArrayList<>(Collections.singletonList(componentBtn2));
202
+        uut.applyChildOptions(a, child);
203
+        List<TitleBarButtonController> initialButtons = uut.getComponentButtons(child);
204
+        forEach(initialButtons, ViewController::ensureViewIsCreated);
205
+
206
+        Options b = new Options();
207
+        b.topBar.buttons.left = new ArrayList<>(Collections.singletonList(componentBtn2));
208
+        uut.mergeChildOptions(b, new Options(), child);
209
+        assertThat(initialButtons.get(0).isDestroyed()).isFalse();
210
+        assertThat(initialButtons.get(1).isDestroyed()).isTrue();
211
+    }
212
+
147 213
     @Test
148 214
     public void mergeTopBarOptions() {
149 215
         Options options = new Options();
@@ -253,16 +319,16 @@ public class StackOptionsPresenterTest extends BaseTest {
253 319
         options.topBar.buttons.left.add(leftButton);
254 320
 
255 321
         uut.applyChildOptions(options, child);
256
-        ArgumentCaptor<List<Button>> rightCaptor = ArgumentCaptor.forClass(List.class);
322
+        ArgumentCaptor<List<TitleBarButtonController>> rightCaptor = ArgumentCaptor.forClass(List.class);
257 323
         verify(topBar).setRightButtons(rightCaptor.capture());
258
-        assertThat(rightCaptor.getValue().get(0).color.get()).isEqualTo(options.topBar.rightButtonColor.get());
259
-        assertThat(rightCaptor.getValue().get(1).color.get()).isEqualTo(options.topBar.rightButtonColor.get());
324
+        assertThat(rightCaptor.getValue().get(0).getButton().color.get()).isEqualTo(options.topBar.rightButtonColor.get());
325
+        assertThat(rightCaptor.getValue().get(1).getButton().color.get()).isEqualTo(options.topBar.rightButtonColor.get());
260 326
         assertThat(rightCaptor.getValue().get(0)).isNotEqualTo(rightButton1);
261 327
         assertThat(rightCaptor.getValue().get(1)).isNotEqualTo(rightButton2);
262 328
 
263
-        ArgumentCaptor<List<Button>> leftCaptor = ArgumentCaptor.forClass(List.class);
329
+        ArgumentCaptor<List<TitleBarButtonController>> leftCaptor = ArgumentCaptor.forClass(List.class);
264 330
         verify(topBar).setLeftButtons(leftCaptor.capture());
265
-        assertThat(leftCaptor.getValue().get(0).color).isEqualTo(options.topBar.leftButtonColor);
331
+        assertThat(leftCaptor.getValue().get(0).getButton().color).isEqualTo(options.topBar.leftButtonColor);
266 332
         assertThat(leftCaptor.getValue().get(0)).isNotEqualTo(leftButton);
267 333
     }
268 334
 
@@ -285,16 +351,16 @@ public class StackOptionsPresenterTest extends BaseTest {
285 351
         options2.topBar.buttons.left.add(leftButton);
286 352
 
287 353
         uut.mergeChildOptions(options2, appliedOptions, child);
288
-        ArgumentCaptor<List<Button>> rightCaptor = ArgumentCaptor.forClass(List.class);
354
+        ArgumentCaptor<List<TitleBarButtonController>> rightCaptor = ArgumentCaptor.forClass(List.class);
289 355
         verify(topBar, times(1)).setRightButtons(rightCaptor.capture());
290
-        assertThat(rightCaptor.getValue().get(0).color.get()).isEqualTo(appliedOptions.topBar.rightButtonColor.get());
291
-        assertThat(rightCaptor.getValue().get(1).color.get()).isEqualTo(appliedOptions.topBar.rightButtonColor.get());
356
+        assertThat(rightCaptor.getValue().get(0).getButton().color.get()).isEqualTo(appliedOptions.topBar.rightButtonColor.get());
357
+        assertThat(rightCaptor.getValue().get(1).getButton().color.get()).isEqualTo(appliedOptions.topBar.rightButtonColor.get());
292 358
         assertThat(rightCaptor.getValue().get(0)).isNotEqualTo(rightButton1);
293 359
         assertThat(rightCaptor.getValue().get(1)).isNotEqualTo(rightButton2);
294 360
 
295
-        ArgumentCaptor<List<Button>> leftCaptor = ArgumentCaptor.forClass(List.class);
361
+        ArgumentCaptor<List<TitleBarButtonController>> leftCaptor = ArgumentCaptor.forClass(List.class);
296 362
         verify(topBar, times(1)).setLeftButtons(leftCaptor.capture());
297
-        assertThat(leftCaptor.getValue().get(0).color.get()).isEqualTo(appliedOptions.topBar.leftButtonColor.get());
363
+        assertThat(leftCaptor.getValue().get(0).getButton().color.get()).isEqualTo(appliedOptions.topBar.leftButtonColor.get());
298 364
         assertThat(leftCaptor.getValue().get(0)).isNotEqualTo(leftButton);
299 365
     }
300 366
 
@@ -317,19 +383,95 @@ public class StackOptionsPresenterTest extends BaseTest {
317 383
         options2.topBar.buttons.left.add(leftButton);
318 384
 
319 385
         uut.mergeChildOptions(options2, resolvedOptions, child);
320
-        ArgumentCaptor<List<Button>> rightCaptor = ArgumentCaptor.forClass(List.class);
386
+        ArgumentCaptor<List<TitleBarButtonController>> rightCaptor = ArgumentCaptor.forClass(List.class);
321 387
         verify(topBar).setRightButtons(rightCaptor.capture());
322
-        assertThat(rightCaptor.getValue().get(0).color.get()).isEqualTo(resolvedOptions.topBar.rightButtonColor.get());
323
-        assertThat(rightCaptor.getValue().get(1).color.get()).isEqualTo(resolvedOptions.topBar.rightButtonColor.get());
388
+        assertThat(rightCaptor.getValue().get(0).getButton().color.get()).isEqualTo(resolvedOptions.topBar.rightButtonColor.get());
389
+        assertThat(rightCaptor.getValue().get(1).getButton().color.get()).isEqualTo(resolvedOptions.topBar.rightButtonColor.get());
324 390
         assertThat(rightCaptor.getValue().get(0)).isNotEqualTo(rightButton1);
325 391
         assertThat(rightCaptor.getValue().get(1)).isNotEqualTo(rightButton2);
326 392
 
327
-        ArgumentCaptor<List<Button>> leftCaptor = ArgumentCaptor.forClass(List.class);
393
+        ArgumentCaptor<List<TitleBarButtonController>> leftCaptor = ArgumentCaptor.forClass(List.class);
328 394
         verify(topBar).setLeftButtons(leftCaptor.capture());
329
-        assertThat(leftCaptor.getValue().get(0).color.get()).isEqualTo(resolvedOptions.topBar.leftButtonColor.get());
395
+        assertThat(leftCaptor.getValue().get(0).getButton().color.get()).isEqualTo(resolvedOptions.topBar.leftButtonColor.get());
330 396
         assertThat(leftCaptor.getValue().get(0)).isNotEqualTo(leftButton);
331 397
     }
332 398
 
399
+    @Test
400
+    public void getButtonControllers_buttonControllersArePassedToTopBar() {
401
+        Options options = new Options();
402
+        options.topBar.buttons.right = new ArrayList<>(Collections.singletonList(textBtn1));
403
+        options.topBar.buttons.left = new ArrayList<>(Collections.singletonList(textBtn1));
404
+        uut.applyChildOptions(options, child);
405
+
406
+        ArgumentCaptor<List<TitleBarButtonController>> rightCaptor = ArgumentCaptor.forClass(List.class);
407
+        ArgumentCaptor<List<TitleBarButtonController>> leftCaptor = ArgumentCaptor.forClass(List.class);
408
+        verify(topBar).setRightButtons(rightCaptor.capture());
409
+        verify(topBar).setLeftButtons(leftCaptor.capture());
410
+
411
+        assertThat(rightCaptor.getValue().size()).isOne();
412
+        assertThat(leftCaptor.getValue().size()).isOne();
413
+    }
414
+
415
+    @Test
416
+    public void getButtonControllers_storesButtonsByComponent() {
417
+        Options options = new Options();
418
+        options.topBar.buttons.right = new ArrayList<>(Collections.singletonList(textBtn1));
419
+        options.topBar.buttons.left = new ArrayList<>(Collections.singletonList(textBtn2));
420
+        uut.applyChildOptions(options, child);
421
+
422
+        List<TitleBarButtonController> componentButtons = uut.getComponentButtons(child);
423
+        assertThat(componentButtons.size()).isEqualTo(2);
424
+        assertThat(componentButtons.get(0).getButton().text.get()).isEqualTo(textBtn1.text.get());
425
+        assertThat(componentButtons.get(1).getButton().text.get()).isEqualTo(textBtn2.text.get());
426
+    }
427
+
428
+    @Test
429
+    public void getButtonControllers_createdOnce() {
430
+        Options options = new Options();
431
+        options.topBar.buttons.right = new ArrayList<>(Collections.singletonList(textBtn1));
432
+        options.topBar.buttons.left = new ArrayList<>(Collections.singletonList(textBtn2));
433
+
434
+        uut.applyChildOptions(options, child);
435
+        List<TitleBarButtonController> buttons1 = uut.getComponentButtons(child);
436
+
437
+        uut.applyChildOptions(options, child);
438
+        List<TitleBarButtonController> buttons2 = uut.getComponentButtons(child);
439
+        for (int i = 0; i < 2; i++) {
440
+            assertThat(buttons1.get(i)).isEqualTo(buttons2.get(i));
441
+        }
442
+    }
443
+
444
+    @Test
445
+    public void applyButtons_doesNotDestroyOtherComponentButtons() {
446
+        Options options = new Options();
447
+        options.topBar.buttons.right = new ArrayList<>(Collections.singletonList(componentBtn1));
448
+        options.topBar.buttons.left = new ArrayList<>(Collections.singletonList(componentBtn2));
449
+        uut.applyChildOptions(options, child);
450
+        List<TitleBarButtonController> buttons = uut.getComponentButtons(child);
451
+        forEach(buttons, ViewController::ensureViewIsCreated);
452
+
453
+        uut.applyChildOptions(options, otherChild);
454
+        for (TitleBarButtonController button : buttons) {
455
+            assertThat(button.isDestroyed()).isFalse();
456
+        }
457
+    }
458
+
459
+    @Test
460
+    public void onChildDestroyed_destroyedButtons() {
461
+        Options options = new Options();
462
+        options.topBar.buttons.right = new ArrayList<>(Collections.singletonList(componentBtn1));
463
+        options.topBar.buttons.left = new ArrayList<>(Collections.singletonList(componentBtn2));
464
+        uut.applyChildOptions(options, child);
465
+        List<TitleBarButtonController> buttons = uut.getComponentButtons(child);
466
+        forEach(buttons, ViewController::ensureViewIsCreated);
467
+
468
+        uut.onChildDestroyed(child);
469
+        for (TitleBarButtonController button : buttons) {
470
+            assertThat(button.isDestroyed()).isTrue();
471
+        }
472
+        assertThat(uut.getComponentButtons(child, null)).isNull();
473
+    }
474
+
333 475
     private void assertTopBarOptions(Options options, int t) {
334 476
         if (options.topBar.title.component.hasValue()) {
335 477
             verify(topBar, times(0)).setTitle(any());

+ 11
- 48
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TitleBarTest.java View File

@@ -4,12 +4,11 @@ import android.app.Activity;
4 4
 import android.view.View;
5 5
 
6 6
 import com.reactnativenavigation.BaseTest;
7
-import com.reactnativenavigation.mocks.ImageLoaderMock;
8
-import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
9 7
 import com.reactnativenavigation.parse.params.Button;
10 8
 import com.reactnativenavigation.parse.params.Text;
11 9
 import com.reactnativenavigation.react.Constants;
12 10
 import com.reactnativenavigation.react.ReactView;
11
+import com.reactnativenavigation.utils.CollectionUtils;
13 12
 import com.reactnativenavigation.views.titlebar.TitleBar;
14 13
 
15 14
 import org.junit.Test;
@@ -21,9 +20,9 @@ import java.util.HashMap;
21 20
 import java.util.List;
22 21
 import java.util.Map;
23 22
 
23
+import static com.reactnativenavigation.utils.TitleBarHelper.createButtonController;
24 24
 import static org.assertj.core.api.Java6Assertions.assertThat;
25 25
 import static org.mockito.Mockito.spy;
26
-import static org.mockito.Mockito.times;
27 26
 import static org.mockito.Mockito.verify;
28 27
 
29 28
 public class TitleBarTest extends BaseTest {
@@ -32,23 +31,15 @@ public class TitleBarTest extends BaseTest {
32 31
     private Button leftButton;
33 32
     private Button textButton;
34 33
     private Button customButton;
35
-    private Map<String, TopBarButtonController> buttonControllers;
34
+    private Map<String, TitleBarButtonController> buttonControllers;
36 35
     private Activity activity;
37 36
 
38 37
     @Override
39 38
     public void beforeEach() {
40
-        final TopBarButtonCreatorMock buttonCreator = new TopBarButtonCreatorMock();
41 39
         activity = newActivity();
42 40
         createButtons();
43 41
         buttonControllers = new HashMap<>();
44
-        uut = spy(new TitleBar(activity, buttonCreator, buttonId -> {}, ImageLoaderMock.mock()) {
45
-            @Override
46
-            public TopBarButtonController createButtonController(Button button) {
47
-                TopBarButtonController controller = spy(super.createButtonController(button));
48
-                buttonControllers.put(button.id, controller);
49
-                return controller;
50
-            }
51
-        });
42
+        uut = spy(new TitleBar(activity));
52 43
     }
53 44
 
54 45
     private void createButtons() {
@@ -80,42 +71,12 @@ public class TitleBarTest extends BaseTest {
80 71
         assertThat(btnView.getComponentName()).isEqualTo(customButton.component.name.get());
81 72
     }
82 73
 
83
-    @Test
84
-    public void destroy_destroysButtonControllers() {
85
-        uut.setLeftButtons(leftButton(leftButton));
86
-        uut.setRightButtons(rightButtons(customButton, textButton));
87
-        uut.clear();
88
-        for (TopBarButtonController controller : buttonControllers.values()) {
89
-            verify(controller, times(1)).destroy();
90
-        }
91
-    }
92
-
93
-    @Test
94
-    public void setRightButtons_destroysRightButtons() {
95
-        uut.setRightButtons(rightButtons(customButton));
96
-        uut.setLeftButtons(leftButton(leftButton));
97
-        uut.setRightButtons(rightButtons(textButton));
98
-        verify(buttonControllers.get(customButton.id), times(1)).destroy();
99
-    }
100
-
101
-    @Test
102
-    public void setRightButtons_onlyDestroysRightButtons() {
103
-        uut.setLeftButtons(leftButton(leftButton));
104
-        uut.setRightButtons(rightButtons(customButton));
105
-        uut.setLeftButtons(null);
106
-        uut.setRightButtons(rightButtons(textButton));
107
-        verify(buttonControllers.get(leftButton.id), times(0)).destroy();
108
-    }
109
-
110 74
     @Test
111 75
     public void setRightButtons_emptyButtonsListClearsRightButtons() {
112 76
         uut.setLeftButtons(new ArrayList<>());
113 77
         uut.setRightButtons(rightButtons(customButton, textButton));
114 78
         uut.setLeftButtons(new ArrayList<>());
115 79
         uut.setRightButtons(new ArrayList<>());
116
-        for (TopBarButtonController controller : buttonControllers.values()) {
117
-            verify(controller, times(1)).destroy();
118
-        }
119 80
         assertThat(uut.getMenu().size()).isEqualTo(0);
120 81
     }
121 82
 
@@ -123,9 +84,11 @@ public class TitleBarTest extends BaseTest {
123 84
     public void setLeftButtons_emptyButtonsListClearsLeftButton() {
124 85
         uut.setLeftButtons(leftButton(leftButton));
125 86
         uut.setRightButtons(rightButtons(customButton));
87
+        assertThat(uut.getNavigationIcon()).isNotNull();
88
+
126 89
         uut.setLeftButtons(new ArrayList<>());
127 90
         uut.setRightButtons(rightButtons(textButton));
128
-        verify(buttonControllers.get(leftButton.id), times(1)).destroy();
91
+        assertThat(uut.getNavigationIcon()).isNull();
129 92
     }
130 93
 
131 94
     @Test
@@ -155,11 +118,11 @@ public class TitleBarTest extends BaseTest {
155 118
         verify(uut).removeView(title);
156 119
     }
157 120
 
158
-    private List<Button> leftButton(Button leftButton) {
159
-        return Collections.singletonList(leftButton);
121
+    private List<TitleBarButtonController> leftButton(Button leftButton) {
122
+        return Collections.singletonList(createButtonController(activity, uut, leftButton));
160 123
     }
161 124
 
162
-    private List<Button> rightButtons(Button... buttons) {
163
-        return Arrays.asList(buttons);
125
+    private List<TitleBarButtonController> rightButtons(Button... buttons) {
126
+        return CollectionUtils.map(Arrays.asList(buttons), button -> createButtonController(activity, uut, button));
164 127
     }
165 128
 }

+ 2
- 2
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopBarButtonControllerTest.java View File

@@ -32,7 +32,7 @@ import static org.mockito.Mockito.verify;
32 32
 
33 33
 public class TopBarButtonControllerTest extends BaseTest {
34 34
 
35
-    private TopBarButtonController uut;
35
+    private TitleBarButtonController uut;
36 36
     private StackController stackController;
37 37
     private Button button;
38 38
     private ButtonOptionsPresenter optionsPresenter;
@@ -53,7 +53,7 @@ public class TopBarButtonControllerTest extends BaseTest {
53 53
         getTitleBar().layout(0, 0, 1080, 200);
54 54
 
55 55
         optionsPresenter = spy(new ButtonOptionsPresenter(getTitleBar(), button));
56
-        uut = new TopBarButtonController(activity, new NavigationIconResolver(activity, ImageLoaderMock.mock()), ImageLoaderMock.mock(), optionsPresenter, button, buttonCreatorMock, (buttonId) -> {});
56
+        uut = new TitleBarButtonController(activity, new NavigationIconResolver(activity, ImageLoaderMock.mock()), ImageLoaderMock.mock(), optionsPresenter, button, buttonCreatorMock, (buttonId) -> {});
57 57
 
58 58
         stackController.ensureViewIsCreated();
59 59
     }

+ 5
- 7
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopBarControllerTest.java View File

@@ -6,8 +6,6 @@ import android.support.annotation.NonNull;
6 6
 
7 7
 import com.reactnativenavigation.BaseTest;
8 8
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
9
-import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
10
-import com.reactnativenavigation.utils.ImageLoader;
11 9
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
12 10
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
13 11
 import com.reactnativenavigation.views.StackLayout;
@@ -36,18 +34,18 @@ public class TopBarControllerTest extends BaseTest {
36 34
         uut = new TopBarController() {
37 35
             @NonNull
38 36
             @Override
39
-            protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout, ImageLoader imageLoader) {
40
-                return new TopBar(context, buttonCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout, imageLoader) {
37
+            protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
38
+                return new TopBar(context, topBarBackgroundViewController, stackLayout) {
41 39
                     @Override
42
-                    protected TitleBar createTitleBar(Context context, ReactViewCreator buttonCreator, TopBarButtonController.OnClickListener onClickListener, ImageLoader imageLoader) {
43
-                        titleBar[0] = spy(super.createTitleBar(context, buttonCreator, onClickListener, imageLoader));
40
+                    protected TitleBar createTitleBar(Context context) {
41
+                        titleBar[0] = spy(super.createTitleBar(context));
44 42
                         return titleBar[0];
45 43
                     }
46 44
                 };
47 45
             }
48 46
         };
49 47
         Activity activity = newActivity();
50
-        uut.createView(activity, new TopBarButtonCreatorMock(), new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()), buttonId -> {}, Mockito.mock(StackLayout.class));
48
+        uut.createView(activity, new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()), Mockito.mock(StackLayout.class));
51 49
         uut.clear();
52 50
         verify(titleBar[0], times(1)).clear();
53 51
     }

+ 8
- 13
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.java View File

@@ -16,16 +16,14 @@ import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
16 16
 import com.reactnativenavigation.parse.NestedAnimationsOptions;
17 17
 import com.reactnativenavigation.parse.Options;
18 18
 import com.reactnativenavigation.parse.params.Bool;
19
-import com.reactnativenavigation.parse.params.Button;
20 19
 import com.reactnativenavigation.parse.params.Text;
21 20
 import com.reactnativenavigation.presentation.StackOptionsPresenter;
22 21
 import com.reactnativenavigation.utils.CommandListenerAdapter;
23 22
 import com.reactnativenavigation.utils.ImageLoader;
23
+import com.reactnativenavigation.utils.TitleBarHelper;
24 24
 import com.reactnativenavigation.utils.ViewHelper;
25 25
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
26 26
 import com.reactnativenavigation.viewcontrollers.ParentController;
27
-import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
28
-import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
29 27
 import com.reactnativenavigation.viewcontrollers.ViewController;
30 28
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
31 29
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
@@ -76,7 +74,7 @@ public class StackControllerTest extends BaseTest {
76 74
         backButtonHelper = spy(new BackButtonHelper());
77 75
         activity = newActivity();
78 76
         childRegistry = new ChildControllersRegistry();
79
-        presenter = spy(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new Options()));
77
+        presenter = spy(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), ImageLoaderMock.mock(), new Options()));
80 78
         child1 = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
81 79
         child2 = spy(new SimpleViewController(activity, childRegistry, "child2", new Options()));
82 80
         child3 = spy(new SimpleViewController(activity, childRegistry, "child3", new Options()));
@@ -130,10 +128,7 @@ public class StackControllerTest extends BaseTest {
130 128
         disablePushAnimation(child1, child2);
131 129
         uut.push(child1, new CommandListenerAdapter());
132 130
 
133
-        Button leftButton = new Button();
134
-        leftButton.id = "someButton";
135
-        leftButton.icon = new Text("icon.png");
136
-        child2.options.topBar.buttons.left = new ArrayList<>(Collections.singleton(leftButton));
131
+        child2.options.topBar.buttons.left = new ArrayList<>(Collections.singleton(TitleBarHelper.iconButton("someButton", "icon.png")));
137 132
 
138 133
         uut.push(child2, new CommandListenerAdapter());
139 134
         child2.onViewAppeared();
@@ -238,7 +233,7 @@ public class StackControllerTest extends BaseTest {
238 233
                         .setTopBarController(new TopBarController())
239 234
                         .setId("uut")
240 235
                         .setInitialOptions(new Options())
241
-                        .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new Options()))
236
+                        .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new Options()))
242 237
                         .build();
243 238
         uut.ensureViewIsCreated();
244 239
         uut.push(child1, new CommandListenerAdapter());
@@ -763,7 +758,7 @@ public class StackControllerTest extends BaseTest {
763 758
                         .setTopBarController(new TopBarController())
764 759
                         .setId("stack")
765 760
                         .setInitialOptions(new Options())
766
-                        .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new Options()))
761
+                        .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new Options()))
767 762
                         .build());
768 763
         Options optionsToMerge = new Options();
769 764
         Component component = mock(Component.class);
@@ -779,7 +774,7 @@ public class StackControllerTest extends BaseTest {
779 774
                         .setTopBarController(new TopBarController())
780 775
                         .setId("stack")
781 776
                         .setInitialOptions(new Options())
782
-                        .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new Options()))
777
+                        .setStackPresenter(new StackOptionsPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new Options()))
783 778
                         .build();
784 779
         ParentController parentController = Mockito.mock(ParentController.class);
785 780
         uut.setParentController(parentController);
@@ -856,8 +851,8 @@ public class StackControllerTest extends BaseTest {
856 851
     private void createTopBarController() {
857 852
         topBarController = spy(new TopBarController() {
858 853
             @Override
859
-            protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout, ImageLoader imageLoader) {
860
-                TopBar spy = spy(super.createTopBar(context, buttonCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout, ImageLoaderMock.mock()));
854
+            protected TopBar createTopBar(Context context, TopBarBackgroundViewController topBarBackgroundViewController, StackLayout stackLayout) {
855
+                TopBar spy = spy(super.createTopBar(context, topBarBackgroundViewController, stackLayout));
861 856
                 spy.layout(0, 0, 1000, 100);
862 857
                 return spy;
863 858
             }

+ 2
- 12
lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarBackgroundComponentTest.java View File

@@ -1,19 +1,15 @@
1 1
 package com.reactnativenavigation.views;
2 2
 
3 3
 import android.app.Activity;
4
-import android.util.Log;
5 4
 import android.view.View;
6 5
 import android.view.ViewGroup;
7 6
 
8 7
 import com.reactnativenavigation.BaseTest;
9 8
 import com.reactnativenavigation.R;
10
-import com.reactnativenavigation.mocks.ImageLoaderMock;
11 9
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
12
-import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
13 10
 import com.reactnativenavigation.parse.Component;
14 11
 import com.reactnativenavigation.parse.params.Text;
15 12
 import com.reactnativenavigation.utils.ViewUtils;
16
-import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
17 13
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
18 14
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
19 15
 import com.reactnativenavigation.views.topbar.TopBar;
@@ -33,16 +29,10 @@ public class TopBarBackgroundComponentTest extends BaseTest {
33 29
     @SuppressWarnings("Convert2Lambda")
34 30
     @Override
35 31
     public void beforeEach() {
36
-        TopBarButtonController.OnClickListener onClickListener = spy(new TopBarButtonController.OnClickListener() {
37
-            @Override
38
-            public void onPress(String buttonId) {
39
-                Log.i("TopBarTest", "onPress: " + buttonId);
40
-            }
41
-        });
42 32
         Activity activity = newActivity();
43 33
         topBarBackgroundViewController = spy(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()));
44
-        StackLayout parent = new StackLayout(activity, new TopBarButtonCreatorMock(), topBarBackgroundViewController, new TopBarController(), onClickListener, null);
45
-        uut = new TopBar(activity, new TopBarButtonCreatorMock(), topBarBackgroundViewController, onClickListener, parent, ImageLoaderMock.mock());
34
+        StackLayout parent = new StackLayout(activity, topBarBackgroundViewController, new TopBarController(), null);
35
+        uut = new TopBar(activity, topBarBackgroundViewController, parent);
46 36
         parent.addView(uut);
47 37
     }
48 38
 

+ 2
- 45
lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarTest.java View File

@@ -1,28 +1,17 @@
1 1
 package com.reactnativenavigation.views;
2 2
 
3 3
 import android.app.Activity;
4
-import android.util.Log;
5
-import android.view.MenuItem;
6 4
 
7 5
 import com.reactnativenavigation.BaseTest;
8 6
 import com.reactnativenavigation.anim.TopBarAnimator;
9
-import com.reactnativenavigation.mocks.ImageLoaderMock;
10 7
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
11
-import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
12 8
 import com.reactnativenavigation.parse.AnimationOptions;
13
-import com.reactnativenavigation.parse.params.Button;
14
-import com.reactnativenavigation.parse.params.Number;
15
-import com.reactnativenavigation.parse.params.Text;
16
-import com.reactnativenavigation.utils.TitleBarHelper;
17
-import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
18 9
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
19 10
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
20 11
 import com.reactnativenavigation.views.topbar.TopBar;
21 12
 
22 13
 import org.junit.Test;
23 14
 
24
-import java.util.ArrayList;
25
-
26 15
 import static org.assertj.core.api.Java6Assertions.assertThat;
27 16
 import static org.mockito.ArgumentMatchers.any;
28 17
 import static org.mockito.ArgumentMatchers.eq;
@@ -34,40 +23,19 @@ public class TopBarTest extends BaseTest {
34 23
 
35 24
     private TopBar uut;
36 25
     private TopBarAnimator animator;
37
-    private ArrayList<Button> rightButtons;
38
-    private TopBarButtonController.OnClickListener onClickListener;
39 26
 
40 27
     @SuppressWarnings("Convert2Lambda")
41 28
     @Override
42 29
     public void beforeEach() {
43
-        onClickListener = spy(new TopBarButtonController.OnClickListener() {
44
-            @Override
45
-            public void onPress(String buttonId) {
46
-                Log.i("TopBarTest", "onPress: " + buttonId);
47
-            }
48
-        });
49 30
         Activity activity = newActivity();
50 31
         TopBarBackgroundViewController topBarBackgroundViewController = new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock());
51
-        StackLayout parent = new StackLayout(activity, new TopBarButtonCreatorMock(), topBarBackgroundViewController, new TopBarController(), this.onClickListener, null);
52
-        uut = new TopBar(activity, new TopBarButtonCreatorMock(), topBarBackgroundViewController, this.onClickListener, parent, ImageLoaderMock.mock());
32
+        StackLayout parent = new StackLayout(activity, topBarBackgroundViewController, new TopBarController(), null);
33
+        uut = new TopBar(activity, topBarBackgroundViewController, parent);
53 34
         animator = spy(new TopBarAnimator(uut));
54 35
         uut.setAnimator(animator);
55
-        rightButtons = createRightButtons();
56 36
         parent.addView(uut);
57 37
     }
58 38
 
59
-    private ArrayList<Button> createRightButtons() {
60
-        ArrayList<Button> result = new ArrayList<>();
61
-        for (int i = 0; i < 2; i++) {
62
-            Button button = new Button();
63
-            button.id = "rightButtons" + i;
64
-            button.text = new Text("btn" + i);
65
-            button.showAsAction = new Number(MenuItem.SHOW_AS_ACTION_ALWAYS);
66
-            result.add(spy(button));
67
-        }
68
-        return result;
69
-    }
70
-
71 39
     @Test
72 40
     public void title() {
73 41
         assertThat(uut.getTitle()).isEmpty();
@@ -89,15 +57,4 @@ public class TopBarTest extends BaseTest {
89 57
         uut.showAnimate(options);
90 58
         verify(animator, times(1)).show(options);
91 59
     }
92
-
93
-    @Test
94
-    public void button_TitleBarButtonOnClickInvoked() {
95
-        uut.setLeftButtons(new ArrayList<>());
96
-        uut.setRightButtons(rightButtons);
97
-        for (int i = 0; i < rightButtons.size(); i++) {
98
-            Button rightButton = rightButtons.get(i);
99
-            TitleBarHelper.getRightButton(uut.getTitleBar(), i).callOnClick();
100
-            verify(onClickListener, times(1)).onPress(rightButton.id);
101
-        }
102
-    }
103 60
 }

+ 5
- 6
lib/android/app/src/test/java/com/reactnativenavigation/views/element/TransitionAnimatorCreatorTest.java View File

@@ -1,7 +1,6 @@
1 1
 package com.reactnativenavigation.views.element;
2 2
 
3 3
 import android.app.Activity;
4
-import android.view.View;
5 4
 
6 5
 import com.reactnativenavigation.BaseTest;
7 6
 import com.reactnativenavigation.parse.Transition;
@@ -13,7 +12,7 @@ import org.mockito.Mockito;
13 12
 import java.util.Arrays;
14 13
 import java.util.List;
15 14
 
16
-import static com.reactnativenavigation.utils.CollectionUtils.map;
15
+import static com.reactnativenavigation.utils.CollectionUtils.keyBy;
17 16
 import static com.reactnativenavigation.views.element.TransitionTestUtils.createElement;
18 17
 import static com.reactnativenavigation.views.element.TransitionTestUtils.createTransition;
19 18
 import static org.mockito.Mockito.spy;
@@ -56,8 +55,8 @@ public class TransitionAnimatorCreatorTest extends BaseTest {
56 55
         TransitionAnimatorCreator spy = spy(uut);
57 56
         spy.create(
58 57
                 Arrays.asList(t1, t2),
59
-                map(Arrays.asList(e1, e3), Element::getElementId),
60
-                map(Arrays.asList(e2, e4), Element::getElementId)
58
+                keyBy(Arrays.asList(e1, e3), Element::getElementId),
59
+                keyBy(Arrays.asList(e2, e4), Element::getElementId)
61 60
         );
62 61
         verify(spy).create(t1, e1, e2);
63 62
         verify(spy).create(t2, e3, e4);
@@ -68,8 +67,8 @@ public class TransitionAnimatorCreatorTest extends BaseTest {
68 67
         List<Transition> transitions = Arrays.asList(t1, t2);
69 68
         uut.create(
70 69
                 transitions,
71
-                map(Arrays.asList(e1, e3), Element::getElementId),
72
-                map(Arrays.asList(e2, e4), Element::getElementId)
70
+                keyBy(Arrays.asList(e1, e3), Element::getElementId),
71
+                keyBy(Arrays.asList(e2, e4), Element::getElementId)
73 72
         );
74 73
         verify(animator1, times(2)).shouldAnimateProperty();
75 74
         verify(animator2, times(2)).shouldAnimateProperty();

+ 13
- 13
lib/android/app/src/test/java/com/reactnativenavigation/views/element/TransitionValidatorTest.java View File

@@ -13,7 +13,7 @@ import org.junit.Test;
13 13
 
14 14
 import java.util.Collections;
15 15
 
16
-import static com.reactnativenavigation.utils.CollectionUtils.map;
16
+import static com.reactnativenavigation.utils.CollectionUtils.keyBy;
17 17
 import static org.assertj.core.api.Java6Assertions.assertThat;
18 18
 
19 19
 public class TransitionValidatorTest extends BaseTest {
@@ -50,8 +50,8 @@ public class TransitionValidatorTest extends BaseTest {
50 50
         transition.toId = new NullText();
51 51
         boolean result = uut.validate(
52 52
                 transition,
53
-                map(Collections.singletonList(from1), Element::getElementId),
54
-                map(Collections.singletonList(from1), Element::getElementId)
53
+                keyBy(Collections.singletonList(from1), Element::getElementId),
54
+                keyBy(Collections.singletonList(from1), Element::getElementId)
55 55
         );
56 56
         assertThat(result).isFalse();
57 57
     }
@@ -61,8 +61,8 @@ public class TransitionValidatorTest extends BaseTest {
61 61
         transition.fromId = new NullText();
62 62
         boolean result = uut.validate(
63 63
                 transition,
64
-                map(Collections.singletonList(from1), Element::getElementId),
65
-                map(Collections.singletonList(from1), Element::getElementId)
64
+                keyBy(Collections.singletonList(from1), Element::getElementId),
65
+                keyBy(Collections.singletonList(from1), Element::getElementId)
66 66
         );
67 67
         assertThat(result).isFalse();
68 68
     }
@@ -71,8 +71,8 @@ public class TransitionValidatorTest extends BaseTest {
71 71
     public void validate_falseIfFromElementRequiredByTransitionDontExist() {
72 72
         boolean result = uut.validate(
73 73
                 invalidFromElementTransition,
74
-                map(Collections.singletonList(from1), Element::getElementId),
75
-                map(Collections.singletonList(to1), Element::getElementId)
74
+                keyBy(Collections.singletonList(from1), Element::getElementId),
75
+                keyBy(Collections.singletonList(to1), Element::getElementId)
76 76
         );
77 77
         assertThat(result).isFalse();
78 78
     }
@@ -81,8 +81,8 @@ public class TransitionValidatorTest extends BaseTest {
81 81
     public void validate_falseIfToElementRequiredByTransitionDontExist() {
82 82
         boolean result = uut.validate(
83 83
                 invalidToElementTransition,
84
-                map(Collections.singletonList(from1), Element::getElementId),
85
-                map(Collections.singletonList(to1), Element::getElementId)
84
+                keyBy(Collections.singletonList(from1), Element::getElementId),
85
+                keyBy(Collections.singletonList(to1), Element::getElementId)
86 86
         );
87 87
         assertThat(result).isFalse();
88 88
     }
@@ -91,8 +91,8 @@ public class TransitionValidatorTest extends BaseTest {
91 91
     public void validate_trueIfElementsRequiredByTransitionExist() {
92 92
         boolean result = uut.validate(
93 93
                 transition,
94
-                map(Collections.singletonList(from1), Element::getElementId),
95
-                map(Collections.singletonList(to1), Element::getElementId)
94
+                keyBy(Collections.singletonList(from1), Element::getElementId),
95
+                keyBy(Collections.singletonList(to1), Element::getElementId)
96 96
         );
97 97
         assertThat(result).isTrue();
98 98
     }
@@ -102,8 +102,8 @@ public class TransitionValidatorTest extends BaseTest {
102 102
         transition.duration = new NullNumber();
103 103
         boolean result = uut.validate(
104 104
                 transition,
105
-                map(Collections.singletonList(from1), Element::getElementId),
106
-                map(Collections.singletonList(to1), Element::getElementId)
105
+                keyBy(Collections.singletonList(from1), Element::getElementId),
106
+                keyBy(Collections.singletonList(to1), Element::getElementId)
107 107
         );
108 108
         assertThat(result).isFalse();
109 109
     }

+ 1
- 1
scripts/test-e2e.js View File

@@ -21,5 +21,5 @@ function run() {
21 21
     if (!skipBuild) {
22 22
         exec.execSync(`detox build --configuration ${configuration}`);
23 23
     }
24
-    exec.execSync(`detox test --configuration ${configuration} --platform ${platform} ${cleanup} ${headless$} ${!android ? `-w ${workers}` : ``}`);
24
+    exec.execSync(`detox test --configuration ${configuration} --platform ${platform} ${cleanup} ${headless$} ${!android ? `-w ${workers}` : ``}`); //--loglevel trace
25 25
 }