Browse Source

V2 back button refactor (#3333)

* Refactored backButton api

* Move StackController to separate package

Implement BackButtonOptions

* Somewhat simplify StackController creation in tests

* Pull backButton logic from StackController

* Handle all TopBar button options in TopBarButtons

* fix back button on iOS
Yogev B 6 years ago
parent
commit
d0ee85ac34
42 changed files with 438 additions and 238 deletions
  1. 6
    4
      docs/docs/styling.md
  2. 55
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/BackButton.java
  3. 3
    3
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java
  4. 40
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarButtons.java
  5. 4
    12
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java
  6. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java
  7. 11
    11
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackOptionsPresenter.java
  8. 1
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java
  9. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java
  10. 4
    3
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java
  11. 14
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/BackButtonHelper.java
  12. 21
    20
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java
  13. 11
    3
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java
  14. 4
    3
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarController.java
  15. 8
    2
      lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBar.java
  16. 12
    4
      lib/android/app/src/main/java/com/reactnativenavigation/views/topbar/TopBar.java
  17. 25
    0
      lib/android/app/src/test/java/com/reactnativenavigation/TestUtils.java
  18. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/utils/OptionHelper.java
  19. 4
    11
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsControllerTest.java
  20. 2
    13
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ComponentViewControllerTest.java
  21. 3
    13
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/FloatingActionButtonTest.java
  22. 4
    11
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java
  23. 12
    12
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java
  24. 2
    2
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsMergingTest.java
  25. 4
    18
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ParentControllerTest.java
  26. 2
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TitleBarTest.java
  27. 6
    13
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopBarButtonControllerTest.java
  28. 5
    4
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopBarControllerTest.java
  29. 5
    21
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java
  30. 4
    14
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ViewControllerTest.java
  31. 68
    0
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/BackButtonHelperTest.java
  32. 32
    21
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.java
  33. 2
    1
      lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarBackgroundComponentTest.java
  34. 2
    1
      lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarTest.java
  35. 11
    0
      lib/ios/RNNBackButtonOptions.h
  36. 26
    0
      lib/ios/RNNBackButtonOptions.m
  37. 0
    1
      lib/ios/RNNNavigationOptions.h
  38. 1
    1
      lib/ios/RNNRootViewController.m
  39. 2
    4
      lib/ios/RNNTopBarOptions.h
  40. 8
    7
      lib/ios/RNNTopBarOptions.m
  41. 8
    0
      lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj
  42. 3
    1
      playground/src/screens/CustomTransitionDestination.js

+ 6
- 4
docs/docs/styling.md View File

@@ -172,10 +172,12 @@ Navigation.mergeOptions(this.props.componentId, {
172 172
     transparent: false,
173 173
     noBorder: false,
174 174
     blur: false,
175
-    backButtonImage: require('icon.png'),
176
-    backButtonHidden: false,
177
-    backButtonTitle: 'Back',
178
-    hideBackButtonTitle: false,
175
+    backButton: {
176
+      image: require('icon.png'),
177
+      visible: true,
178
+      title: 'Back',
179
+      hideTitle: false,
180
+    },
179 181
     largeTitle: {
180 182
       visible: true,
181 183
       fontSize: 30,

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

@@ -0,0 +1,55 @@
1
+package com.reactnativenavigation.parse;
2
+
3
+import com.reactnativenavigation.parse.params.Bool;
4
+import com.reactnativenavigation.parse.params.Button;
5
+import com.reactnativenavigation.parse.params.NullBool;
6
+import com.reactnativenavigation.parse.parsers.BoolParser;
7
+import com.reactnativenavigation.parse.parsers.ColorParser;
8
+import com.reactnativenavigation.parse.parsers.TextParser;
9
+import com.reactnativenavigation.react.Constants;
10
+
11
+import org.json.JSONObject;
12
+
13
+public class BackButton extends Button {
14
+    public static BackButton parse(JSONObject json) {
15
+        BackButton result = new BackButton();
16
+        if (json == null) return result;
17
+
18
+        result.visible = BoolParser.parse(json, "visible");
19
+        if (json.has("icon")) result.icon = TextParser.parse(json.optJSONObject("icon"), "uri");
20
+        result.id = json.optString("id", Constants.BACK_BUTTON_ID);
21
+        result.enabled = BoolParser.parse(json, "enabled");
22
+        result.disableIconTint = BoolParser.parse(json, "disableIconTint");
23
+        result.color = ColorParser.parse(json, "color");
24
+        result.disabledColor = ColorParser.parse(json, "disabledColor");
25
+        result.testId = TextParser.parse(json, "testID");
26
+
27
+        return result;
28
+    }
29
+
30
+    public BackButton() {
31
+        id = Constants.BACK_BUTTON_ID;
32
+    }
33
+
34
+    public Bool visible = new NullBool();
35
+
36
+    public void mergeWith(BackButton other) {
37
+        if (other.icon.hasValue()) icon = other.icon;
38
+        if (other.visible.hasValue()) visible = other.visible;
39
+        if (other.color.hasValue()) color = other.color;
40
+        if (other.disabledColor.hasValue()) disabledColor = other.disabledColor;
41
+        if (other.disableIconTint.hasValue()) disableIconTint = other.disableIconTint;
42
+        if (other.enabled.hasValue()) enabled = other.enabled;
43
+        if (other.testId.hasValue()) testId = other.testId;
44
+    }
45
+
46
+    void mergeWithDefault(final BackButton defaultOptions) {
47
+        if (!icon.hasValue()) icon = defaultOptions.icon;
48
+        if (!visible.hasValue()) visible = defaultOptions.visible;
49
+        if (!color.hasValue()) color = defaultOptions.color;
50
+        if (!disabledColor.hasValue()) disabledColor = defaultOptions.disabledColor;
51
+        if (!disableIconTint.hasValue()) disableIconTint = defaultOptions.disableIconTint;
52
+        if (!enabled.hasValue()) enabled = defaultOptions.enabled;
53
+        if (!testId.hasValue()) testId = defaultOptions.testId;
54
+    }
55
+}

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

@@ -10,8 +10,8 @@ import com.reactnativenavigation.utils.TypefaceLoader;
10 10
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
11 11
 import com.reactnativenavigation.viewcontrollers.ComponentViewController;
12 12
 import com.reactnativenavigation.viewcontrollers.SideMenuController;
13
-import com.reactnativenavigation.viewcontrollers.StackController;
14
-import com.reactnativenavigation.viewcontrollers.StackControllerBuilder;
13
+import com.reactnativenavigation.viewcontrollers.stack.StackController;
14
+import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
15 15
 import com.reactnativenavigation.viewcontrollers.ViewController;
16 16
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabsController;
17 17
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentCreator;
@@ -161,7 +161,7 @@ public class LayoutFactory {
161 161
                 .setTopBarController(new TopBarController())
162 162
                 .setId(node.id)
163 163
                 .setInitialOptions(parseNodeOptions(node))
164
-                .createStackController();
164
+                .build();
165 165
         addChildrenToStack(node.children, stackController);
166 166
         return stackController;
167 167
 	}

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

@@ -0,0 +1,40 @@
1
+package com.reactnativenavigation.parse;
2
+
3
+import android.support.annotation.Nullable;
4
+
5
+import com.reactnativenavigation.parse.params.Button;
6
+import com.reactnativenavigation.utils.TypefaceLoader;
7
+
8
+import org.json.JSONObject;
9
+
10
+import java.util.ArrayList;
11
+
12
+public class TopBarButtons {
13
+
14
+    public static TopBarButtons parse(TypefaceLoader typefaceLoader, JSONObject json) {
15
+        TopBarButtons result = new TopBarButtons();
16
+        if (json == null) return result;
17
+
18
+        result.right = Button.parseJsonArray(json.optJSONArray("rightButtons"), typefaceLoader);
19
+        result.left = Button.parseJsonArray(json.optJSONArray("leftButtons"), typefaceLoader);
20
+        result.back = BackButton.parse(json.optJSONObject("backButton"));
21
+
22
+        return result;
23
+    }
24
+
25
+    public BackButton back = new BackButton();
26
+    @Nullable public ArrayList<Button> left;
27
+    @Nullable public ArrayList<Button> right;
28
+
29
+    void mergeWith(TopBarButtons other) {
30
+        if (other.left != null) left = other.left;
31
+        if (other.right != null) right = other.right;
32
+        back.mergeWith(other.back);
33
+    }
34
+
35
+    void mergeWithDefault(TopBarButtons defaultOptions) {
36
+        if (left == null) left = defaultOptions.left;
37
+        if (right == null) right = defaultOptions.right;
38
+        back.mergeWithDefault(defaultOptions.back);
39
+    }
40
+}

+ 4
- 12
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java View File

@@ -1,12 +1,10 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3 3
 
4
-import android.support.annotation.Nullable;
5 4
 import android.util.Log;
6 5
 
7 6
 import com.reactnativenavigation.BuildConfig;
8 7
 import com.reactnativenavigation.parse.params.Bool;
9
-import com.reactnativenavigation.parse.params.Button;
10 8
 import com.reactnativenavigation.parse.params.Color;
11 9
 import com.reactnativenavigation.parse.params.Fraction;
12 10
 import com.reactnativenavigation.parse.params.NullBool;
@@ -25,8 +23,6 @@ import com.reactnativenavigation.utils.TypefaceLoader;
25 23
 
26 24
 import org.json.JSONObject;
27 25
 
28
-import java.util.ArrayList;
29
-
30 26
 public class TopBarOptions {
31 27
 
32 28
     public static TopBarOptions parse(TypefaceLoader typefaceLoader, JSONObject json) {
@@ -40,13 +36,12 @@ public class TopBarOptions {
40 36
         options.animate = BoolParser.parse(json,"animate");
41 37
         options.hideOnScroll = BoolParser.parse(json,"hideOnScroll");
42 38
         options.drawBehind = BoolParser.parse(json,"drawBehind");
43
-        options.rightButtons = Button.parseJsonArray(json.optJSONArray("rightButtons"), typefaceLoader);
44
-        options.leftButtons = Button.parseJsonArray(json.optJSONArray("leftButtons"), typefaceLoader);
45 39
         options.testId = TextParser.parse(json, "testID");
46 40
         options.height = NumberParser.parse(json, "height");
47 41
         options.borderColor = ColorParser.parse(json, "borderColor");
48 42
         options.borderHeight = FractionParser.parse(json, "borderHeight");
49 43
         options.elevation = FractionParser.parse(json, "elevation");
44
+        options.buttons = TopBarButtons.parse(typefaceLoader, json);
50 45
 
51 46
         options.validate();
52 47
         return options;
@@ -54,6 +49,7 @@ public class TopBarOptions {
54 49
 
55 50
     public TitleOptions title = new TitleOptions();
56 51
     public SubtitleOptions subtitle = new SubtitleOptions();
52
+    public TopBarButtons buttons = new TopBarButtons();
57 53
     public Text testId = new NullText();
58 54
     public TopBarBackgroundOptions background = new TopBarBackgroundOptions();
59 55
     public Bool visible = new NullBool();
@@ -64,20 +60,17 @@ public class TopBarOptions {
64 60
     public Fraction elevation = new NullFraction();
65 61
     public Fraction borderHeight = new NullFraction();
66 62
     public Color borderColor = new NullColor();
67
-    @Nullable public ArrayList<Button> leftButtons;
68
-    @Nullable public ArrayList<Button> rightButtons;
69 63
 
70 64
     void mergeWith(final TopBarOptions other) {
71 65
         title.mergeWith(other.title);
72 66
         subtitle.mergeWith(other.subtitle);
73 67
         background.mergeWith(other.background);
68
+        buttons.mergeWith(other.buttons);
74 69
         if (other.testId.hasValue()) testId = other.testId;
75 70
         if (other.visible.hasValue()) visible = other.visible;
76 71
         if (other.animate.hasValue()) animate = other.animate;
77 72
         if (other.hideOnScroll.hasValue()) hideOnScroll = other.hideOnScroll;
78 73
         if (other.drawBehind.hasValue()) drawBehind = other.drawBehind;
79
-        if (other.leftButtons != null) leftButtons = other.leftButtons;
80
-        if (other.rightButtons != null) rightButtons = other.rightButtons;
81 74
         if (other.height.hasValue()) height = other.height;
82 75
         if (other.borderHeight.hasValue()) borderHeight = other.borderHeight;
83 76
         if (other.borderColor.hasValue()) borderColor = other.borderColor;
@@ -89,12 +82,11 @@ public class TopBarOptions {
89 82
         title.mergeWithDefault(defaultOptions.title);
90 83
         subtitle.mergeWithDefault(defaultOptions.subtitle);
91 84
         background.mergeWithDefault(defaultOptions.background);
85
+        buttons.mergeWithDefault(defaultOptions.buttons);
92 86
         if (!visible.hasValue()) visible = defaultOptions.visible;
93 87
         if (!animate.hasValue()) animate = defaultOptions.animate;
94 88
         if (!hideOnScroll.hasValue()) hideOnScroll = defaultOptions.hideOnScroll;
95 89
         if (!drawBehind.hasValue()) drawBehind = defaultOptions.drawBehind;
96
-        if (leftButtons == null) leftButtons = defaultOptions.leftButtons;
97
-        if (rightButtons == null) rightButtons = defaultOptions.rightButtons;
98 90
         if (!testId.hasValue()) testId = defaultOptions.testId;
99 91
         if (!height.hasValue()) height = defaultOptions.height;
100 92
         if (!borderHeight.hasValue()) borderHeight = defaultOptions.borderHeight;

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

@@ -31,7 +31,7 @@ public class Button {
31 31
     public Text testId = new NullText();
32 32
     public Component component = new Component();
33 33
 
34
-    private static Button parseJson(JSONObject json, TypefaceLoader typefaceManager) {
34
+    protected static Button parseJson(JSONObject json, TypefaceLoader typefaceManager) {
35 35
         Button button = new Button();
36 36
         button.id = json.optString("id");
37 37
         button.title = TextParser.parse(json, "title");

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

@@ -7,17 +7,15 @@ import android.view.ViewGroup.LayoutParams;
7 7
 import com.reactnativenavigation.parse.AnimationsOptions;
8 8
 import com.reactnativenavigation.parse.Options;
9 9
 import com.reactnativenavigation.parse.OrientationOptions;
10
+import com.reactnativenavigation.parse.TopBarButtons;
10 11
 import com.reactnativenavigation.parse.TopBarOptions;
11 12
 import com.reactnativenavigation.parse.TopTabOptions;
12 13
 import com.reactnativenavigation.parse.TopTabsOptions;
13
-import com.reactnativenavigation.parse.params.Button;
14 14
 import com.reactnativenavigation.utils.UiUtils;
15 15
 import com.reactnativenavigation.viewcontrollers.IReactView;
16 16
 import com.reactnativenavigation.views.Component;
17 17
 import com.reactnativenavigation.views.topbar.TopBar;
18 18
 
19
-import java.util.ArrayList;
20
-
21 19
 public class StackOptionsPresenter {
22 20
     private static final int DEFAULT_TITLE_COLOR = Color.BLACK;
23 21
     private static final int DEFAULT_SUBTITLE_COLOR = Color.GRAY;
@@ -35,7 +33,7 @@ public class StackOptionsPresenter {
35 33
 
36 34
     public void applyChildOptions(Options options, Component child) {
37 35
         applyOrientation(options.layout.orientation);
38
-        applyButtons(options.topBar.leftButtons, options.topBar.rightButtons);
36
+        applyButtons(options.topBar.buttons);
39 37
         applyTopBarOptions(options.topBar, options.animations, child, options);
40 38
         applyTopTabsOptions(options.topTabs);
41 39
         applyTopTabOptions(options.topTabOptions);
@@ -98,9 +96,10 @@ public class StackOptionsPresenter {
98 96
         }
99 97
     }
100 98
 
101
-    private void applyButtons(ArrayList<Button> leftButtons, ArrayList<Button> rightButtons) {
102
-        topBar.setLeftButtons(leftButtons);
103
-        topBar.setRightButtons(rightButtons);
99
+    private void applyButtons(TopBarButtons buttons) {
100
+        topBar.setLeftButtons(buttons.left);
101
+        topBar.setRightButtons(buttons.right);
102
+        if (buttons.back.visible.isTrue()) topBar.setBackButton(buttons.back);
104 103
     }
105 104
 
106 105
     private void applyTopTabsOptions(TopTabsOptions options) {
@@ -126,7 +125,7 @@ public class StackOptionsPresenter {
126 125
 
127 126
     public void mergeChildOptions(Options options, Component child) {
128 127
         mergeOrientation(options.layout.orientation);
129
-        mergeButtons(options.topBar.leftButtons, options.topBar.rightButtons);
128
+        mergeButtons(options.topBar.buttons);
130 129
         mergeTopBarOptions(options.topBar, options.animations, child);
131 130
         mergeTopTabsOptions(options.topTabs);
132 131
         mergeTopTabOptions(options.topTabOptions);
@@ -136,9 +135,10 @@ public class StackOptionsPresenter {
136 135
         if (orientationOptions.hasValue()) applyOrientation(orientationOptions);
137 136
     }
138 137
 
139
-    private void mergeButtons(ArrayList<Button> leftButtons, ArrayList<Button> rightButtons) {
140
-        if (leftButtons != null) topBar.setLeftButtons(leftButtons);
141
-        if (rightButtons != null) topBar.setRightButtons(rightButtons);
138
+    private void mergeButtons(TopBarButtons buttons) {
139
+        if (buttons.left != null) topBar.setLeftButtons(buttons.left);
140
+        if (buttons.right != null) topBar.setRightButtons(buttons.right);
141
+        if (buttons.back != null) topBar.setBackButton(buttons.back);
142 142
     }
143 143
 
144 144
     private void mergeTopBarOptions(TopBarOptions options, AnimationsOptions animationsOptions, Component component) {

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

@@ -20,6 +20,7 @@ import com.reactnativenavigation.utils.CompatUtils;
20 20
 import com.reactnativenavigation.utils.NativeCommandListener;
21 21
 import com.reactnativenavigation.viewcontrollers.modal.ModalPresenter;
22 22
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
23
+import com.reactnativenavigation.viewcontrollers.stack.StackController;
23 24
 
24 25
 import java.util.Collection;
25 26
 import java.util.Collections;

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

@@ -79,7 +79,7 @@ public abstract class ParentController<T extends ViewGroup> extends ChildControl
79 79
 	}
80 80
 
81 81
 	@CallSuper
82
-    void clearOptions() {
82
+    protected void clearOptions() {
83 83
 	    applyOnParentController(parent -> ((ParentController) parent).clearOptions());
84 84
         options = initialOptions.copy();
85 85
     }

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

@@ -16,6 +16,7 @@ import com.reactnativenavigation.utils.CommandListener;
16 16
 import com.reactnativenavigation.utils.StringUtils;
17 17
 import com.reactnativenavigation.utils.Task;
18 18
 import com.reactnativenavigation.utils.UiUtils;
19
+import com.reactnativenavigation.viewcontrollers.stack.StackController;
19 20
 import com.reactnativenavigation.views.Component;
20 21
 
21 22
 public abstract class ViewController<T extends ViewGroup> implements ViewTreeObserver.OnGlobalLayoutListener {
@@ -44,7 +45,7 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
44 45
     private boolean isShown;
45 46
     private boolean isDestroyed;
46 47
     private ViewVisibilityListener viewVisibilityListener = new ViewVisibilityListenerAdapter();
47
-    FabOptionsPresenter fabOptionsPresenter;
48
+    protected FabOptionsPresenter fabOptionsPresenter;
48 49
 
49 50
     public ViewController(Activity activity, String id, Options initialOptions) {
50 51
         this.activity = activity;
@@ -93,8 +94,8 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
93 94
         if (parentController != null) task.run(parentController);
94 95
     }
95 96
 
96
-    @Nullable
97
-    ParentController getParentController() {
97
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
98
+    public ParentController getParentController() {
98 99
         return parentController;
99 100
     }
100 101
 

+ 14
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/BackButtonHelper.java View File

@@ -0,0 +1,14 @@
1
+package com.reactnativenavigation.viewcontrollers.stack;
2
+
3
+import com.reactnativenavigation.parse.Options;
4
+import com.reactnativenavigation.parse.params.Bool;
5
+import com.reactnativenavigation.viewcontrollers.ViewController;
6
+
7
+public class BackButtonHelper {
8
+    public void addToChild(StackController stack, ViewController child) {
9
+        if (stack.size() <= 1 || child.options.topBar.buttons.left != null || child.options.topBar.buttons.back.visible.isFalse()) return;
10
+        Options options = new Options();
11
+        options.topBar.buttons.back.visible = new Bool(true);
12
+        child.mergeOptions(options);
13
+    }
14
+}

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java → lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java View File

@@ -1,17 +1,22 @@
1
-package com.reactnativenavigation.viewcontrollers;
1
+package com.reactnativenavigation.viewcontrollers.stack;
2 2
 
3 3
 import android.app.Activity;
4 4
 import android.support.annotation.NonNull;
5 5
 import android.support.annotation.RestrictTo;
6
+import android.support.annotation.VisibleForTesting;
6 7
 import android.support.v4.view.ViewPager;
7 8
 
8 9
 import com.reactnativenavigation.anim.NavigationAnimator;
9 10
 import com.reactnativenavigation.parse.Options;
10
-import com.reactnativenavigation.parse.params.Button;
11 11
 import com.reactnativenavigation.presentation.OptionsPresenter;
12 12
 import com.reactnativenavigation.react.Constants;
13 13
 import com.reactnativenavigation.utils.CommandListener;
14 14
 import com.reactnativenavigation.utils.CommandListenerAdapter;
15
+import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
16
+import com.reactnativenavigation.viewcontrollers.IdStack;
17
+import com.reactnativenavigation.viewcontrollers.ParentController;
18
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
19
+import com.reactnativenavigation.viewcontrollers.ViewController;
15 20
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
16 21
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
17 22
 import com.reactnativenavigation.views.Component;
@@ -20,9 +25,7 @@ import com.reactnativenavigation.views.StackLayout;
20 25
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
21 26
 import com.reactnativenavigation.views.topbar.TopBar;
22 27
 
23
-import java.util.ArrayList;
24 28
 import java.util.Collection;
25
-import java.util.Collections;
26 29
 import java.util.Iterator;
27 30
 
28 31
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -35,14 +38,16 @@ public class StackController extends ParentController<StackLayout> {
35 38
     private final TitleBarReactViewCreator titleBarReactViewCreator;
36 39
     private TopBarBackgroundViewController topBarBackgroundViewController;
37 40
     private TopBarController topBarController;
41
+    private BackButtonHelper backButtonHelper;
38 42
 
39
-    public StackController(Activity activity, ChildControllersRegistry childRegistry, ReactViewCreator topBarButtonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, NavigationAnimator animator, String id, Options initialOptions) {
43
+    public StackController(Activity activity, ChildControllersRegistry childRegistry, ReactViewCreator topBarButtonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, NavigationAnimator animator, String id, Options initialOptions, BackButtonHelper backButtonHelper) {
40 44
         super(activity, childRegistry, id, new OptionsPresenter(activity), initialOptions);
41 45
         this.topBarController = topBarController;
42 46
         this.topBarButtonCreator = topBarButtonCreator;
43 47
         this.titleBarReactViewCreator = titleBarReactViewCreator;
44 48
         this.topBarBackgroundViewController = topBarBackgroundViewController;
45 49
         this.animator = animator;
50
+        this.backButtonHelper = backButtonHelper;
46 51
     }
47 52
 
48 53
     @Override
@@ -94,7 +99,7 @@ public class StackController extends ParentController<StackLayout> {
94 99
     }
95 100
 
96 101
     @Override
97
-    void clearOptions() {
102
+    public void clearOptions() {
98 103
         super.clearOptions();
99 104
         topBarController.clear();
100 105
     }
@@ -122,12 +127,7 @@ public class StackController extends ParentController<StackLayout> {
122 127
     }
123 128
 
124 129
     private void addBackButton(ViewController child) {
125
-        if (size() <= 1 || child.options.topBar.leftButtons != null) return;
126
-        Options options = new Options();
127
-        Button back = new Button();
128
-        back.id = Constants.BACK_BUTTON_ID;
129
-        options.topBar.leftButtons = new ArrayList<>(Collections.singleton(back));
130
-        child.mergeOptions(options);
130
+        backButtonHelper.addToChild(this, child);
131 131
     }
132 132
 
133 133
     public void setRoot(ViewController child, CommandListener listener) {
@@ -150,7 +150,7 @@ public class StackController extends ParentController<StackLayout> {
150 150
         }
151 151
     }
152 152
 
153
-    void pop(CommandListener listener) {
153
+    public void pop(CommandListener listener) {
154 154
         if (!canPop()) {
155 155
             listener.onError("Nothing to pop");
156 156
             return;
@@ -176,7 +176,7 @@ public class StackController extends ParentController<StackLayout> {
176 176
         listener.onSuccess(disappearing.getId());
177 177
     }
178 178
 
179
-    void popSpecific(ViewController childController, CommandListener listener) {
179
+    public void popSpecific(ViewController childController, CommandListener listener) {
180 180
         if (stack.isTop(childController.getId())) {
181 181
             pop(listener);
182 182
         } else {
@@ -185,7 +185,7 @@ public class StackController extends ParentController<StackLayout> {
185 185
         }
186 186
     }
187 187
 
188
-    void popTo(final ViewController viewController, CommandListener listener) {
188
+    public void popTo(final ViewController viewController, CommandListener listener) {
189 189
         if (!stack.containsId(viewController.getId())) {
190 190
             listener.onError("Nothing to pop");
191 191
             return;
@@ -205,7 +205,7 @@ public class StackController extends ParentController<StackLayout> {
205 205
         pop(listener);
206 206
     }
207 207
 
208
-    void popToRoot(CommandListener listener) {
208
+    public void popToRoot(CommandListener listener) {
209 209
         if (!canPop()) {
210 210
             listener.onError("Nothing to pop");
211 211
             return;
@@ -227,7 +227,7 @@ public class StackController extends ParentController<StackLayout> {
227 227
         controller.destroy();
228 228
     }
229 229
 
230
-    ViewController peek() {
230
+    public ViewController peek() {
231 231
         return stack.peek();
232 232
     }
233 233
 
@@ -248,7 +248,8 @@ public class StackController extends ParentController<StackLayout> {
248 248
         return false;
249 249
     }
250 250
 
251
-    boolean canPop() {
251
+    @VisibleForTesting()
252
+    public boolean canPop() {
252 253
         return stack.size() > 1;
253 254
     }
254 255
 
@@ -295,12 +296,12 @@ public class StackController extends ParentController<StackLayout> {
295 296
     }
296 297
 
297 298
     @RestrictTo(RestrictTo.Scope.TESTS)
298
-    TopBar getTopBar() {
299
+    public TopBar getTopBar() {
299 300
         return topBarController.getView();
300 301
     }
301 302
 
302 303
     @RestrictTo(RestrictTo.Scope.TESTS)
303
-    StackLayout getStackLayout() {
304
+    public StackLayout getStackLayout() {
304 305
         return getView();
305 306
     }
306 307
 }

lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackControllerBuilder.java → lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java View File

@@ -1,9 +1,11 @@
1
-package com.reactnativenavigation.viewcontrollers;
1
+package com.reactnativenavigation.viewcontrollers.stack;
2 2
 
3 3
 import android.app.Activity;
4 4
 
5 5
 import com.reactnativenavigation.anim.NavigationAnimator;
6 6
 import com.reactnativenavigation.parse.Options;
7
+import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
8
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
7 9
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
8 10
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
9 11
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
@@ -18,6 +20,7 @@ public class StackControllerBuilder {
18 20
     private String id;
19 21
     private Options initialOptions = new Options();
20 22
     private NavigationAnimator animator;
23
+    private BackButtonHelper backButtonHelper = new BackButtonHelper();
21 24
 
22 25
     public StackControllerBuilder(Activity activity) {
23 26
         this.activity = activity;
@@ -64,7 +67,12 @@ public class StackControllerBuilder {
64 67
         return this;
65 68
     }
66 69
 
67
-    public StackController createStackController() {
68
-        return new StackController(activity, childRegistry, topBarButtonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarController, animator, id, initialOptions);
70
+    public StackControllerBuilder setBackButtonHelper(BackButtonHelper backButtonHelper) {
71
+        this.backButtonHelper = backButtonHelper;
72
+        return this;
73
+    }
74
+
75
+    public StackController build() {
76
+        return new StackController(activity, childRegistry, topBarButtonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarController, animator, id, initialOptions, backButtonHelper);
69 77
     }
70 78
 }

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

@@ -5,6 +5,7 @@ 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;
8 9
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
9 10
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
10 11
 import com.reactnativenavigation.views.StackLayout;
@@ -17,14 +18,14 @@ public class TopBarController {
17 18
 
18 19
     public View createView(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout) {
19 20
         if (topBar == null) {
20
-            topBar = createTopBar(context, buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout);
21
+            topBar = createTopBar(context, buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout, new ImageLoader());
21 22
             topBar.setId(CompatUtils.generateViewId());
22 23
         }
23 24
         return topBar;
24 25
     }
25 26
 
26
-    protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout) {
27
-        return new TopBar(context, buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout);
27
+    protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout, ImageLoader imageLoader) {
28
+        return new TopBar(context, buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout, imageLoader);
28 29
     }
29 30
 
30 31
     public void clear() {

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

@@ -11,6 +11,7 @@ import android.view.ViewGroup;
11 11
 import android.widget.TextView;
12 12
 
13 13
 import com.reactnativenavigation.parse.Alignment;
14
+import com.reactnativenavigation.parse.BackButton;
14 15
 import com.reactnativenavigation.parse.Component;
15 16
 import com.reactnativenavigation.parse.params.Button;
16 17
 import com.reactnativenavigation.parse.params.Color;
@@ -38,11 +39,13 @@ public class TitleBar extends Toolbar {
38 39
     private final TopBarButtonController.OnClickListener onClickListener;
39 40
     private final List<TopBarButtonController> rightButtonControllers = new ArrayList<>();
40 41
     private TopBarButtonController leftButtonController;
42
+    private ImageLoader imageLoader;
41 43
 
42
-    public TitleBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator reactViewCreator, TopBarButtonController.OnClickListener onClickListener) {
44
+    public TitleBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator reactViewCreator, TopBarButtonController.OnClickListener onClickListener, ImageLoader imageLoader) {
43 45
         super(context);
44 46
         this.buttonCreator = buttonCreator;
45 47
         this.reactViewCreator = reactViewCreator;
48
+        this.imageLoader = imageLoader;
46 49
         reactViewController = new TitleBarReactViewController((Activity) context, reactViewCreator);
47 50
         this.onClickListener = onClickListener;
48 51
         getMenu();
@@ -167,6 +170,10 @@ public class TitleBar extends Toolbar {
167 170
         if (getMenu().size() > 0) getMenu().clear();
168 171
     }
169 172
 
173
+    public void setBackButton(BackButton button) {
174
+        setLeftButton(button);
175
+    }
176
+
170 177
     public void setLeftButtons(List<Button> leftButtons) {
171 178
         if (leftButtons == null) return;
172 179
         if (leftButtons.isEmpty()) {
@@ -196,7 +203,6 @@ public class TitleBar extends Toolbar {
196 203
     }
197 204
 
198 205
     public TopBarButtonController createButtonController(Button button) {
199
-        ImageLoader imageLoader = new ImageLoader();
200 206
         return new TopBarButtonController((Activity) getContext(),
201 207
                 new NavigationIconResolver(getContext(), imageLoader),
202 208
                 imageLoader,

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

@@ -25,12 +25,14 @@ import com.reactnativenavigation.anim.TopBarCollapseBehavior;
25 25
 import com.reactnativenavigation.interfaces.ScrollEventListener;
26 26
 import com.reactnativenavigation.parse.Alignment;
27 27
 import com.reactnativenavigation.parse.AnimationOptions;
28
+import com.reactnativenavigation.parse.BackButton;
28 29
 import com.reactnativenavigation.parse.Component;
29 30
 import com.reactnativenavigation.parse.params.Button;
30 31
 import com.reactnativenavigation.parse.params.Color;
31 32
 import com.reactnativenavigation.parse.params.Fraction;
32 33
 import com.reactnativenavigation.parse.params.Number;
33 34
 import com.reactnativenavigation.utils.CompatUtils;
35
+import com.reactnativenavigation.utils.ImageLoader;
34 36
 import com.reactnativenavigation.utils.UiUtils;
35 37
 import com.reactnativenavigation.utils.ViewUtils;
36 38
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
@@ -57,9 +59,11 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
57 59
     private StackLayout parentView;
58 60
     private TopBarBackgroundViewController topBarBackgroundViewController;
59 61
     private View border;
62
+    private ImageLoader imageLoader;
60 63
 
61
-    public TopBar(final Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener onClickListener, StackLayout parentView) {
64
+    public TopBar(final Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener onClickListener, StackLayout parentView, ImageLoader imageLoader) {
62 65
         super(context);
66
+        this.imageLoader = imageLoader;
63 67
         collapsingBehavior = new TopBarCollapseBehavior(this);
64 68
         this.topBarBackgroundViewController = topBarBackgroundViewController;
65 69
         this.parentView = parentView;
@@ -70,7 +74,7 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
70 74
 
71 75
     private void createLayout(ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarButtonController.OnClickListener onClickListener) {
72 76
         setId(CompatUtils.generateViewId());
73
-        titleBar = createTitleBar(getContext(), buttonCreator, titleBarReactViewCreator, onClickListener);
77
+        titleBar = createTitleBar(getContext(), buttonCreator, titleBarReactViewCreator, onClickListener, imageLoader);
74 78
         topTabs = createTopTabs();
75 79
         border = createBorder();
76 80
         content = createContentLayout();
@@ -110,8 +114,8 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
110 114
         return border;
111 115
     }
112 116
 
113
-    protected TitleBar createTitleBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator reactViewCreator, TopBarButtonController.OnClickListener onClickListener) {
114
-        TitleBar titleBar = new TitleBar(context, buttonCreator, reactViewCreator, onClickListener);
117
+    protected TitleBar createTitleBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator reactViewCreator, TopBarButtonController.OnClickListener onClickListener, ImageLoader imageLoader) {
118
+        TitleBar titleBar = new TitleBar(context, buttonCreator, reactViewCreator, onClickListener, imageLoader);
115 119
         titleBar.setId(CompatUtils.generateViewId());
116 120
         return titleBar;
117 121
     }
@@ -209,6 +213,10 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
209 213
         topTabs.setLayoutParams(topTabs.getLayoutParams());
210 214
     }
211 215
 
216
+    public void setBackButton(BackButton backButton) {
217
+        titleBar.setBackButton(backButton);
218
+    }
219
+
212 220
     public void setLeftButtons(List<Button> leftButtons) {
213 221
         titleBar.setLeftButtons(leftButtons);
214 222
     }

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

@@ -0,0 +1,25 @@
1
+package com.reactnativenavigation;
2
+
3
+import android.app.Activity;
4
+
5
+import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
6
+import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
7
+import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
8
+import com.reactnativenavigation.parse.Options;
9
+import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
10
+import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
11
+import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
12
+import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
13
+
14
+public class TestUtils {
15
+    public static StackControllerBuilder newStackController(Activity activity) {
16
+        return new StackControllerBuilder(activity)
17
+                .setId("stack")
18
+                .setChildRegistry(new ChildControllersRegistry())
19
+                .setTopBarButtonCreator(new TopBarButtonCreatorMock())
20
+                .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
21
+                .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
22
+                .setTopBarController(new TopBarController())
23
+                .setInitialOptions(new Options());
24
+    }
25
+}

+ 1
- 1
lib/android/app/src/test/java/com/reactnativenavigation/utils/OptionHelper.java View File

@@ -8,7 +8,7 @@ import java.util.ArrayList;
8 8
 public class OptionHelper {
9 9
     public static Options createBottomTabOptions() {
10 10
         Options options = new Options();
11
-        options.topBar.leftButtons = new ArrayList<>();
11
+        options.topBar.buttons.left = new ArrayList<>();
12 12
         options.bottomTabOptions.title = new Text("Tab");
13 13
         options.bottomTabOptions.icon = new Text("http://127.0.0.1/icon.png");
14 14
         return options;

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

@@ -6,11 +6,9 @@ import android.view.ViewGroup;
6 6
 import android.widget.RelativeLayout;
7 7
 
8 8
 import com.reactnativenavigation.BaseTest;
9
+import com.reactnativenavigation.TestUtils;
9 10
 import com.reactnativenavigation.mocks.ImageLoaderMock;
10 11
 import com.reactnativenavigation.mocks.SimpleViewController;
11
-import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
12
-import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
13
-import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
14 12
 import com.reactnativenavigation.parse.Options;
15 13
 import com.reactnativenavigation.parse.params.Bool;
16 14
 import com.reactnativenavigation.parse.params.Color;
@@ -21,8 +19,7 @@ import com.reactnativenavigation.utils.CommandListenerAdapter;
21 19
 import com.reactnativenavigation.utils.ImageLoader;
22 20
 import com.reactnativenavigation.utils.OptionHelper;
23 21
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabsController;
24
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
25
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
22
+import com.reactnativenavigation.viewcontrollers.stack.StackController;
26 23
 import com.reactnativenavigation.views.BottomTabs;
27 24
 import com.reactnativenavigation.views.ReactComponent;
28 25
 
@@ -234,14 +231,10 @@ public class BottomTabsControllerTest extends BaseTest {
234 231
     }
235 232
 
236 233
     private StackController createStack(String id) {
237
-        return new StackControllerBuilder(activity)
238
-                .setTopBarButtonCreator(new TopBarButtonCreatorMock())
239
-                .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
240
-                .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
241
-                .setTopBarController(new TopBarController())
234
+        return TestUtils.newStackController(activity)
242 235
                 .setId(id)
243 236
                 .setInitialOptions(tabOptions)
244
-                .createStackController();
237
+                .build();
245 238
     }
246 239
 
247 240
     private ViewGroup.MarginLayoutParams childLayoutParams(int index) {

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

@@ -3,14 +3,10 @@ package com.reactnativenavigation.viewcontrollers;
3 3
 import android.app.Activity;
4 4
 
5 5
 import com.reactnativenavigation.BaseTest;
6
+import com.reactnativenavigation.TestUtils;
6 7
 import com.reactnativenavigation.mocks.TestComponentLayout;
7 8
 import com.reactnativenavigation.mocks.TestReactView;
8
-import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
9
-import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
10
-import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
11 9
 import com.reactnativenavigation.parse.Options;
12
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
13
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
14 10
 import com.reactnativenavigation.views.StackLayout;
15 11
 
16 12
 import org.junit.Test;
@@ -30,14 +26,7 @@ public class ComponentViewControllerTest extends BaseTest {
30 26
         super.beforeEach();
31 27
         Activity activity = newActivity();
32 28
         view = spy(new TestComponentLayout(activity, new TestReactView(activity)));
33
-        ParentController<StackLayout> parentController = new StackControllerBuilder(activity)
34
-                .setTopBarButtonCreator(new TopBarButtonCreatorMock())
35
-                .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
36
-                .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
37
-                .setTopBarController(new TopBarController())
38
-                .setId("stack")
39
-                .setInitialOptions(new Options())
40
-                .createStackController();
29
+        ParentController<StackLayout> parentController = TestUtils.newStackController(activity).build();
41 30
         uut = new ComponentViewController(activity, new ChildControllersRegistry(), "componentId1", "componentName", (activity1, componentId, componentName) -> view, new Options());
42 31
         uut.setParentController(parentController);
43 32
         parentController.ensureViewIsCreated();

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

@@ -6,16 +6,13 @@ import android.view.View;
6 6
 import android.view.ViewGroup;
7 7
 
8 8
 import com.reactnativenavigation.BaseTest;
9
+import com.reactnativenavigation.TestUtils;
9 10
 import com.reactnativenavigation.mocks.SimpleViewController;
10
-import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
11
-import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
12
-import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
13 11
 import com.reactnativenavigation.parse.FabOptions;
14 12
 import com.reactnativenavigation.parse.Options;
15 13
 import com.reactnativenavigation.parse.params.Text;
16 14
 import com.reactnativenavigation.utils.CommandListenerAdapter;
17
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
18
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
15
+import com.reactnativenavigation.viewcontrollers.stack.StackController;
19 16
 import com.reactnativenavigation.views.Fab;
20 17
 import com.reactnativenavigation.views.FabMenu;
21 18
 import com.reactnativenavigation.views.StackLayout;
@@ -39,14 +36,7 @@ public class FloatingActionButtonTest extends BaseTest {
39 36
         super.beforeEach();
40 37
         activity = newActivity();
41 38
         childRegistry = new ChildControllersRegistry();
42
-        stackController = new StackControllerBuilder(activity)
43
-                .setTopBarButtonCreator(new TopBarButtonCreatorMock())
44
-                .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
45
-                .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
46
-                .setTopBarController(new TopBarController())
47
-                .setId("stackController")
48
-                .setInitialOptions(new Options())
49
-                .createStackController();
39
+        stackController = TestUtils.newStackController(activity).build();
50 40
         Options options = getOptionsWithFab();
51 41
         childFab = new SimpleViewController(activity, childRegistry, "child1", options);
52 42
         childNoFab = new SimpleViewController(activity, childRegistry, "child2", new Options());

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

@@ -4,12 +4,10 @@ import android.support.annotation.NonNull;
4 4
 
5 5
 import com.reactnativenavigation.BaseTest;
6 6
 import com.reactnativenavigation.TestActivity;
7
+import com.reactnativenavigation.TestUtils;
7 8
 import com.reactnativenavigation.mocks.ImageLoaderMock;
8 9
 import com.reactnativenavigation.mocks.SimpleComponentViewController;
9 10
 import com.reactnativenavigation.mocks.SimpleViewController;
10
-import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
11
-import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
12
-import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
13 11
 import com.reactnativenavigation.parse.Options;
14 12
 import com.reactnativenavigation.parse.params.Bool;
15 13
 import com.reactnativenavigation.parse.params.Text;
@@ -21,8 +19,7 @@ import com.reactnativenavigation.utils.CompatUtils;
21 19
 import com.reactnativenavigation.utils.ImageLoader;
22 20
 import com.reactnativenavigation.utils.OptionHelper;
23 21
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabsController;
24
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
25
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
22
+import com.reactnativenavigation.viewcontrollers.stack.StackController;
26 23
 
27 24
 import org.junit.Test;
28 25
 import org.mockito.Mockito;
@@ -279,15 +276,11 @@ public class NavigatorTest extends BaseTest {
279 276
 
280 277
     @NonNull
281 278
     private StackController newStack() {
282
-        return new StackControllerBuilder(activity)
279
+        return TestUtils.newStackController(activity)
283 280
                 .setChildRegistry(childRegistry)
284
-                .setTopBarButtonCreator(new TopBarButtonCreatorMock())
285
-                .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
286
-                .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
287
-                .setTopBarController(new TopBarController())
288 281
                 .setId("stack" + CompatUtils.generateViewId())
289 282
                 .setInitialOptions(tabOptions)
290
-                .createStackController();
283
+                .build();
291 284
     }
292 285
 
293 286
     @Test

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

@@ -8,6 +8,7 @@ import android.view.View;
8 8
 import android.widget.RelativeLayout;
9 9
 
10 10
 import com.reactnativenavigation.BaseTest;
11
+import com.reactnativenavigation.TestUtils;
11 12
 import com.reactnativenavigation.mocks.TestComponentLayout;
12 13
 import com.reactnativenavigation.mocks.TestReactView;
13 14
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
@@ -21,6 +22,9 @@ import com.reactnativenavigation.parse.params.Bool;
21 22
 import com.reactnativenavigation.parse.params.Fraction;
22 23
 import com.reactnativenavigation.parse.params.Text;
23 24
 import com.reactnativenavigation.utils.CommandListenerAdapter;
25
+import com.reactnativenavigation.utils.ImageLoader;
26
+import com.reactnativenavigation.viewcontrollers.stack.StackController;
27
+import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
24 28
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
25 29
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
26 30
 import com.reactnativenavigation.views.StackLayout;
@@ -44,7 +48,6 @@ public class OptionsApplyingTest extends BaseTest {
44 48
     private ComponentViewController uut;
45 49
     private IReactView view;
46 50
     private Options initialNavigationOptions;
47
-    private TopBarController topBarController;
48 51
     private TopBar topBar;
49 52
 
50 53
     @Override
@@ -61,27 +64,24 @@ public class OptionsApplyingTest extends BaseTest {
61 64
                 (activity1, componentId, componentName) -> view,
62 65
                 initialNavigationOptions
63 66
         );
64
-        topBarController = new TopBarController() {
67
+        TopBarController topBarController = new TopBarController() {
65 68
             @Override
66
-            protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout) {
67
-                topBar = spy(super.createTopBar(context, buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout));
69
+            protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout, ImageLoader imageLoader) {
70
+                topBar =
71
+                        spy(super.createTopBar(context, buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout, imageLoader));
68 72
                 return topBar;
69 73
             }
70 74
         };
71
-        stackController = new StackControllerBuilder(activity)
72
-                .setTopBarButtonCreator(new TopBarButtonCreatorMock())
73
-                .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
74
-                .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
75
+        stackController = TestUtils.newStackController(activity)
75 76
                 .setTopBarController(topBarController)
76
-                .setId("stack")
77
-                .setInitialOptions(new Options())
78
-                .createStackController();
77
+                .build();
79 78
         stackController.ensureViewIsCreated();
80 79
         stackController.getView().layout(0, 0, 1000, 1000);
81 80
         stackController.getTopBar().layout(0, 0, 1000, 100);
82 81
         uut.setParentController(stackController);
83 82
     }
84 83
 
84
+    @SuppressWarnings("ConstantConditions")
85 85
     @Test
86 86
     public void applyNavigationOptionsHandlesNoParentStack() {
87 87
         uut.setParentController(null);
@@ -102,7 +102,7 @@ public class OptionsApplyingTest extends BaseTest {
102 102
                         .setTopBarController(new TopBarController())
103 103
                         .setId("stackId")
104 104
                         .setInitialOptions(new Options())
105
-                        .createStackController();
105
+                        .build();
106 106
         stackController.push(uut, new CommandListenerAdapter());
107 107
         assertThat(stackController.getTopBar().getTitle()).isEmpty();
108 108
 

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

@@ -67,11 +67,11 @@ public class OptionsMergingTest extends BaseTest {
67 67
         verify(topBar, times(0)).setRightButtons(any());
68 68
         verify(topBar, times(0)).setLeftButtons(any());
69 69
 
70
-        options.topBar.rightButtons = new ArrayList<>();
70
+        options.topBar.buttons.right = new ArrayList<>();
71 71
         uut.mergeChildOptions(options, child);
72 72
         verify(topBar, times(1)).setRightButtons(any());
73 73
 
74
-        options.topBar.leftButtons = new ArrayList<>();
74
+        options.topBar.buttons.left = new ArrayList<>();
75 75
         uut.mergeChildOptions(options, child);
76 76
         verify(topBar, times(1)).setLeftButtons(any());
77 77
     }

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

@@ -6,16 +6,13 @@ import android.view.ViewGroup;
6 6
 import android.widget.FrameLayout;
7 7
 
8 8
 import com.reactnativenavigation.BaseTest;
9
+import com.reactnativenavigation.TestUtils;
9 10
 import com.reactnativenavigation.mocks.SimpleViewController;
10
-import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
11
-import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
12
-import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
13 11
 import com.reactnativenavigation.parse.Options;
14 12
 import com.reactnativenavigation.parse.params.Text;
15 13
 import com.reactnativenavigation.presentation.OptionsPresenter;
16 14
 import com.reactnativenavigation.utils.CommandListenerAdapter;
17
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
18
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
15
+import com.reactnativenavigation.viewcontrollers.stack.StackController;
19 16
 import com.reactnativenavigation.views.ReactComponent;
20 17
 
21 18
 import org.junit.Test;
@@ -98,7 +95,7 @@ public class ParentControllerTest extends BaseTest {
98 95
 
99 96
     @Test
100 97
     public void findControllerById_Recursive() {
101
-        StackController stackController = createStack();
98
+        StackController stackController = TestUtils.newStackController(activity).build();
102 99
         SimpleViewController child1 = new SimpleViewController(activity, childRegistry, "child1", new Options());
103 100
         SimpleViewController child2 = new SimpleViewController(activity, childRegistry, "child2", new Options());
104 101
         stackController.push(child1, new CommandListenerAdapter());
@@ -120,7 +117,7 @@ public class ParentControllerTest extends BaseTest {
120 117
 
121 118
     @Test
122 119
     public void optionsAreClearedWhenChildIsAppeared() {
123
-        StackController stackController = spy(createStack());
120
+        StackController stackController = spy(TestUtils.newStackController(activity).build());
124 121
         SimpleViewController child1 = new SimpleViewController(activity, childRegistry, "child1", new Options());
125 122
         stackController.push(child1, new CommandListenerAdapter());
126 123
 
@@ -178,15 +175,4 @@ public class ParentControllerTest extends BaseTest {
178 175
         uut.applyChildOptions(options, child1.getView());
179 176
         verify(presenter, times(0)).applyRootOptions(uut.getView(), options);
180 177
     }
181
-
182
-    private StackController createStack() {
183
-        return new StackControllerBuilder(activity)
184
-                .setTopBarButtonCreator(new TopBarButtonCreatorMock())
185
-                .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
186
-                .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
187
-                .setTopBarController(new TopBarController())
188
-                .setId("stack")
189
-                .setInitialOptions(new Options())
190
-                .createStackController();
191
-    }
192 178
 }

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

@@ -6,6 +6,7 @@ import android.view.Gravity;
6 6
 import android.view.ViewGroup;
7 7
 
8 8
 import com.reactnativenavigation.BaseTest;
9
+import com.reactnativenavigation.mocks.ImageLoaderMock;
9 10
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
10 11
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
11 12
 import com.reactnativenavigation.parse.Alignment;
@@ -49,7 +50,7 @@ public class TitleBarTest extends BaseTest {
49 50
         createButtons();
50 51
         buttonControllers = new HashMap<>();
51 52
         TitleBarReactViewCreatorMock reactViewCreator = new TitleBarReactViewCreatorMock();
52
-        uut = spy(new TitleBar(activity, buttonCreator, reactViewCreator, (buttonId -> {})) {
53
+        uut = spy(new TitleBar(activity, buttonCreator, reactViewCreator, (buttonId -> {}), ImageLoaderMock.mock()) {
53 54
             @Override
54 55
             public TopBarButtonController createButtonController(Button button) {
55 56
                 TopBarButtonController controller = spy(super.createButtonController(button));

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

@@ -6,11 +6,9 @@ import android.support.v7.widget.Toolbar;
6 6
 import android.view.MenuItem;
7 7
 
8 8
 import com.reactnativenavigation.BaseTest;
9
+import com.reactnativenavigation.TestUtils;
9 10
 import com.reactnativenavigation.mocks.ImageLoaderMock;
10
-import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
11
-import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
12 11
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
13
-import com.reactnativenavigation.parse.Options;
14 12
 import com.reactnativenavigation.parse.params.Bool;
15 13
 import com.reactnativenavigation.parse.params.Button;
16 14
 import com.reactnativenavigation.parse.params.Color;
@@ -19,8 +17,7 @@ import com.reactnativenavigation.parse.params.Number;
19 17
 import com.reactnativenavigation.parse.params.Text;
20 18
 import com.reactnativenavigation.utils.ButtonOptionsPresenter;
21 19
 import com.reactnativenavigation.viewcontrollers.button.NavigationIconResolver;
22
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
23
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
20
+import com.reactnativenavigation.viewcontrollers.stack.StackController;
24 21
 
25 22
 import org.junit.Test;
26 23
 
@@ -45,14 +42,10 @@ public class TopBarButtonControllerTest extends BaseTest {
45 42
         final Activity activity = newActivity();
46 43
 
47 44
         TopBarButtonCreatorMock buttonCreatorMock = new TopBarButtonCreatorMock();
48
-        stackController = spy(new StackControllerBuilder(activity)
49
-                .setTopBarButtonCreator(buttonCreatorMock)
50
-                .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
51
-                .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
52
-                .setTopBarController(new TopBarController())
53
-                .setId("stack")
54
-                .setInitialOptions(new Options())
55
-                .createStackController()
45
+        stackController = spy(
46
+                TestUtils.newStackController(activity)
47
+                        .setTopBarButtonCreator(buttonCreatorMock)
48
+                        .build()
56 49
         );
57 50
         stackController.getView().layout(0, 0, 1080, 1920);
58 51
         stackController.getTopBar().layout(0, 0, 1080, 200);

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

@@ -8,6 +8,7 @@ import com.reactnativenavigation.BaseTest;
8 8
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
9 9
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
10 10
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
11
+import com.reactnativenavigation.utils.ImageLoader;
11 12
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
12 13
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
13 14
 import com.reactnativenavigation.views.StackLayout;
@@ -37,11 +38,11 @@ public class TopBarControllerTest extends BaseTest {
37 38
         uut = new TopBarController() {
38 39
             @NonNull
39 40
             @Override
40
-            protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout) {
41
-                return new TopBar(context, buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout) {
41
+            protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout, ImageLoader imageLoader) {
42
+                return new TopBar(context, buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout, imageLoader) {
42 43
                     @Override
43
-                    protected TitleBar createTitleBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator reactViewCreator, TopBarButtonController.OnClickListener onClickListener) {
44
-                        titleBar[0] = spy(super.createTitleBar(context, buttonCreator, reactViewCreator, onClickListener));
44
+                    protected TitleBar createTitleBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator reactViewCreator, TopBarButtonController.OnClickListener onClickListener, ImageLoader imageLoader) {
45
+                        titleBar[0] = spy(super.createTitleBar(context, buttonCreator, reactViewCreator, onClickListener, imageLoader));
45 46
                         return titleBar[0];
46 47
                     }
47 48
                 };

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

@@ -5,18 +5,15 @@ import android.support.annotation.NonNull;
5 5
 import android.view.ViewGroup;
6 6
 
7 7
 import com.reactnativenavigation.BaseTest;
8
+import com.reactnativenavigation.TestUtils;
8 9
 import com.reactnativenavigation.mocks.TestComponentViewCreator;
9 10
 import com.reactnativenavigation.mocks.TestReactView;
10
-import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
11
-import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
12
-import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
13 11
 import com.reactnativenavigation.parse.Options;
14 12
 import com.reactnativenavigation.parse.params.Bool;
15 13
 import com.reactnativenavigation.parse.params.Text;
16 14
 import com.reactnativenavigation.utils.CommandListenerAdapter;
17 15
 import com.reactnativenavigation.utils.ViewHelper;
18
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
19
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
16
+import com.reactnativenavigation.viewcontrollers.stack.StackController;
20 17
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsAdapter;
21 18
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
22 19
 import com.reactnativenavigation.views.ReactComponent;
@@ -43,7 +40,6 @@ public class TopTabsViewControllerTest extends BaseTest {
43 40
     private StackController stack;
44 41
     private TopTabsController uut;
45 42
     private List<ViewController> tabControllers = new ArrayList<>(SIZE);
46
-    private List<Options> tabOptions = new ArrayList<>(SIZE);
47 43
     private final Options options = new Options();
48 44
     private TopTabsViewPager topTabsLayout;
49 45
     private Activity activity;
@@ -55,7 +51,7 @@ public class TopTabsViewControllerTest extends BaseTest {
55 51
 
56 52
         activity = newActivity();
57 53
         childRegistry = new ChildControllersRegistry();
58
-        tabOptions = createOptions();
54
+        List<Options> tabOptions = createOptions();
59 55
         tabControllers = createTabsControllers(activity, tabOptions);
60 56
 
61 57
         topTabsLayout = spy(new TopTabsViewPager(activity, tabControllers, new TopTabsAdapter(tabControllers)));
@@ -64,23 +60,11 @@ public class TopTabsViewControllerTest extends BaseTest {
64 60
         uut = spy(new TopTabsController(activity, childRegistry, "componentId", tabControllers, layoutCreator, options));
65 61
         tabControllers.forEach(viewController -> viewController.setParentController(uut));
66 62
 
67
-        stack = spy(createStackController("stackId"));
63
+        stack = spy(TestUtils.newStackController(activity).build());
68 64
         stack.push(uut, new CommandListenerAdapter());
69 65
         uut.setParentController(stack);
70 66
     }
71 67
 
72
-    @NonNull
73
-    private StackController createStackController(String id) {
74
-        return new StackControllerBuilder(activity)
75
-                .setTopBarButtonCreator(new TopBarButtonCreatorMock())
76
-                .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
77
-                .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
78
-                .setTopBarController(new TopBarController())
79
-                .setId(id)
80
-                .setInitialOptions(new Options())
81
-                .createStackController();
82
-    }
83
-
84 68
     @NonNull
85 69
     private ArrayList<Options> createOptions() {
86 70
         ArrayList result = new ArrayList();
@@ -222,7 +206,7 @@ public class TopTabsViewControllerTest extends BaseTest {
222 206
     public void applyOptions_tabsAreRemovedAfterViewDisappears() {
223 207
         stack.getView().removeAllViews();
224 208
 
225
-        StackController stackController = spy(createStackController("stack"));
209
+        StackController stackController = spy(TestUtils.newStackController(activity).build());
226 210
         ComponentViewController first = new ComponentViewController(
227 211
                 activity,
228 212
                 childRegistry,

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

@@ -7,14 +7,11 @@ import android.widget.FrameLayout;
7 7
 import android.widget.LinearLayout;
8 8
 
9 9
 import com.reactnativenavigation.BaseTest;
10
+import com.reactnativenavigation.TestUtils;
10 11
 import com.reactnativenavigation.mocks.SimpleViewController;
11
-import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
12
-import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
13
-import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
14 12
 import com.reactnativenavigation.parse.Options;
15 13
 import com.reactnativenavigation.utils.CommandListenerAdapter;
16
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
17
-import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
14
+import com.reactnativenavigation.viewcontrollers.stack.StackController;
18 15
 
19 16
 import org.assertj.android.api.Assertions;
20 17
 import org.junit.Test;
@@ -71,20 +68,13 @@ public class ViewControllerTest extends BaseTest {
71 68
         assertThat(myController.getView()).isEqualTo(otherView);
72 69
     }
73 70
 
71
+    @SuppressWarnings("ConstantConditions")
74 72
     @Test
75 73
     public void holdsAReferenceToStackControllerOrNull() {
76
-        //noinspection ConstantConditions
77 74
         uut.setParentController(null);
78 75
 
79 76
         assertThat(uut.getParentController()).isNull();
80
-        StackController nav = new StackControllerBuilder(activity)
81
-                .setTopBarButtonCreator(new TopBarButtonCreatorMock())
82
-                .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
83
-                .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
84
-                .setTopBarController(new TopBarController())
85
-                .setId("stack")
86
-                .setInitialOptions(new Options())
87
-                .createStackController();
77
+        StackController nav = TestUtils.newStackController(activity).build();
88 78
         nav.push(uut, new CommandListenerAdapter());
89 79
         assertThat(uut.getParentController()).isEqualTo(nav);
90 80
     }

+ 68
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/BackButtonHelperTest.java View File

@@ -0,0 +1,68 @@
1
+package com.reactnativenavigation.viewcontrollers.stack;
2
+
3
+import android.app.Activity;
4
+
5
+import com.reactnativenavigation.BaseTest;
6
+import com.reactnativenavigation.TestUtils;
7
+import com.reactnativenavigation.mocks.SimpleViewController;
8
+import com.reactnativenavigation.parse.Options;
9
+import com.reactnativenavigation.parse.params.Bool;
10
+import com.reactnativenavigation.utils.CommandListenerAdapter;
11
+import com.reactnativenavigation.viewcontrollers.ChildController;
12
+import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
13
+
14
+import org.junit.Test;
15
+import org.mockito.ArgumentCaptor;
16
+
17
+import static org.assertj.core.api.Java6Assertions.assertThat;
18
+import static org.mockito.ArgumentMatchers.any;
19
+import static org.mockito.Mockito.spy;
20
+import static org.mockito.Mockito.times;
21
+import static org.mockito.Mockito.verify;
22
+
23
+public class BackButtonHelperTest extends BaseTest {
24
+    private BackButtonHelper uut;
25
+    private StackController stack;
26
+    private ChildController child1;
27
+    private ChildController child2;
28
+
29
+    @Override
30
+    public void beforeEach() {
31
+        uut = new BackButtonHelper();
32
+        Activity activity = newActivity();
33
+        ChildControllersRegistry childRegistry = new ChildControllersRegistry();
34
+        stack = TestUtils.newStackController(activity)
35
+                .setChildRegistry(childRegistry)
36
+                .setBackButtonHelper(uut)
37
+                .build();
38
+        child1 = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
39
+        child2 = spy(new SimpleViewController(activity, childRegistry, "child2", new Options()));
40
+    }
41
+
42
+    @Test
43
+    public void addToChild_doesNotAddIfStackContainsOneChild() {
44
+        uut.addToChild(stack, child1);
45
+        verify(child1, times(0)).mergeOptions(any());
46
+    }
47
+
48
+    @Test
49
+    public void addToChild_addsIfStackContainsMoreThenOneChild() {
50
+        disablePushAnimation(child1, child2);
51
+        stack.push(child1, new CommandListenerAdapter());
52
+        stack.push(child2, new CommandListenerAdapter());
53
+
54
+        ArgumentCaptor<Options> optionWithBackButton = ArgumentCaptor.forClass(Options.class);
55
+        verify(child2, times(1)).mergeOptions(optionWithBackButton.capture());
56
+        assertThat(optionWithBackButton.getValue().topBar.buttons.back.visible.get()).isTrue();
57
+    }
58
+
59
+    @Test
60
+    public void addToChild_doesNotAddIfBackButtonHidden() {
61
+        disablePushAnimation(child1, child2);
62
+        stack.push(child1, new CommandListenerAdapter());
63
+        child2.options.topBar.buttons.back.visible = new Bool(false);
64
+        stack.push(child2, new CommandListenerAdapter());
65
+
66
+        verify(child2, times(0)).mergeOptions(any());
67
+    }
68
+}

lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackControllerTest.java → lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.java View File

@@ -1,11 +1,13 @@
1
-package com.reactnativenavigation.viewcontrollers;
1
+package com.reactnativenavigation.viewcontrollers.stack;
2 2
 
3 3
 import android.app.Activity;
4 4
 import android.content.Context;
5 5
 import android.view.View;
6 6
 
7 7
 import com.reactnativenavigation.BaseTest;
8
+import com.reactnativenavigation.TestUtils;
8 9
 import com.reactnativenavigation.anim.NavigationAnimator;
10
+import com.reactnativenavigation.mocks.ImageLoaderMock;
9 11
 import com.reactnativenavigation.mocks.SimpleViewController;
10 12
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
11 13
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
@@ -16,7 +18,13 @@ import com.reactnativenavigation.parse.params.Bool;
16 18
 import com.reactnativenavigation.parse.params.Button;
17 19
 import com.reactnativenavigation.parse.params.Text;
18 20
 import com.reactnativenavigation.utils.CommandListenerAdapter;
21
+import com.reactnativenavigation.utils.ImageLoader;
19 22
 import com.reactnativenavigation.utils.ViewHelper;
23
+import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
24
+import com.reactnativenavigation.viewcontrollers.ParentController;
25
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
26
+import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
27
+import com.reactnativenavigation.viewcontrollers.ViewController;
20 28
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
21 29
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
22 30
 import com.reactnativenavigation.views.Component;
@@ -95,28 +103,34 @@ public class StackControllerTest extends BaseTest {
95 103
     public void push_backButtonIsAddedIfStackContainsMoreThenOneScreen() {
96 104
         uut.push(child1, new CommandListenerAdapter());
97 105
         verify(child1, times(0)).mergeOptions(any());
106
+        assertThat(child1.options.topBar.buttons.back.visible.isFalseOrUndefined()).isTrue();
98 107
 
99 108
         uut.push(child2, new CommandListenerAdapter());
100 109
         ArgumentCaptor<Options> captor = ArgumentCaptor.forClass(Options.class);
101 110
         verify(child2, times(1)).mergeOptions(captor.capture());
102
-        assertThat(captor.getValue().topBar.leftButtons).isNotNull();
103
-        assertThat(captor.getValue().topBar.leftButtons.get(0).id).isEqualTo("RNN.back");
111
+        assertThat(captor.getValue().topBar.buttons.back.visible.get()).isTrue();
104 112
     }
105 113
 
106 114
     @Test
107
-    public void push_backButtonIsNotAddedIfScreenContainsLeftButtons() {
115
+    public void push_backButtonIsNotAddedIfScreenContainsLeftButton() {
116
+        disablePushAnimation(child1, child2);
108 117
         uut.push(child1, new CommandListenerAdapter());
109 118
 
110 119
         Button leftButton = new Button();
111 120
         leftButton.id = "someButton";
112
-        child2.options.topBar.leftButtons = new ArrayList<>(Collections.singleton(leftButton));
121
+        leftButton.icon = new Text("icon.png");
122
+        child2.options.topBar.buttons.left = new ArrayList<>(Collections.singleton(leftButton));
123
+
113 124
         uut.push(child2, new CommandListenerAdapter());
114
-        verify(child2, times(0)).mergeOptions(any());
125
+        child2.onViewAppeared();
126
+        assertThat(topBarController.getView().getTitleBar().getNavigationIcon()).isNotNull();
127
+        verify(topBarController.getView(), times(1)).setLeftButtons(any());
128
+        verify(topBarController.getView(), times(0)).setBackButton(any());
115 129
     }
116 130
 
117 131
     @Test
118 132
     public void push_backButtonIsNotAddedIfScreenClearsLeftButton() {
119
-        child1.options.topBar.leftButtons = new ArrayList<>();
133
+        child1.options.topBar.buttons.left = new ArrayList<>();
120 134
         uut.push(child1, new CommandListenerAdapter());
121 135
         verify(child1, times(0)).mergeOptions(any());
122 136
     }
@@ -136,6 +150,7 @@ public class StackControllerTest extends BaseTest {
136 150
 
137 151
     @Test
138 152
     public void setRoot() {
153
+        disablePushAnimation(child1, child2);
139 154
         assertThat(uut.isEmpty()).isTrue();
140 155
         uut.push(child1, new CommandListenerAdapter());
141 156
         uut.push(child2, new CommandListenerAdapter());
@@ -184,7 +199,7 @@ public class StackControllerTest extends BaseTest {
184 199
                         .setTopBarController(new TopBarController())
185 200
                         .setId("uut")
186 201
                         .setInitialOptions(new Options())
187
-                        .createStackController();
202
+                        .build();
188 203
         uut.push(child1, new CommandListenerAdapter());
189 204
         uut.push(child2, new CommandListenerAdapter() {
190 205
             @Override
@@ -680,7 +695,7 @@ public class StackControllerTest extends BaseTest {
680 695
                         .setTopBarController(new TopBarController())
681 696
                         .setId("stack")
682 697
                         .setInitialOptions(new Options())
683
-                        .createStackController());
698
+                        .build());
684 699
         Options optionsToMerge = new Options();
685 700
         Component component = mock(Component.class);
686 701
         uut.mergeChildOptions(optionsToMerge, component);
@@ -696,7 +711,7 @@ public class StackControllerTest extends BaseTest {
696 711
                         .setTopBarController(new TopBarController())
697 712
                         .setId("stack")
698 713
                         .setInitialOptions(new Options())
699
-                        .createStackController();
714
+                        .build();
700 715
         ParentController parentController = Mockito.mock(ParentController.class);
701 716
         uut.setParentController(parentController);
702 717
         Options optionsToMerge = new Options();
@@ -753,28 +768,24 @@ public class StackControllerTest extends BaseTest {
753 768
     }
754 769
 
755 770
     private StackController createStackController() {
756
-        return createStackController("stackId");
771
+        return createStackController("stack");
757 772
     }
758 773
 
759 774
     private StackController createStackController(String id) {
760 775
         createTopBarController();
761
-        return new StackControllerBuilder(activity)
762
-                .setChildRegistry(childRegistry)
763
-                .setTopBarButtonCreator(new TopBarButtonCreatorMock())
764
-                .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
765
-                .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
776
+        return TestUtils.newStackController(activity)
777
+                .setId(id)
766 778
                 .setTopBarController(topBarController)
779
+                .setChildRegistry(childRegistry)
767 780
                 .setAnimator(animator)
768
-                .setId(id)
769
-                .setInitialOptions(new Options())
770
-                .createStackController();
781
+                .build();
771 782
     }
772 783
 
773 784
     private void createTopBarController() {
774 785
         topBarController = spy(new TopBarController() {
775 786
             @Override
776
-            protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout) {
777
-                return spy(super.createTopBar(context, buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout));
787
+            protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout, ImageLoader imageLoader) {
788
+                return spy(super.createTopBar(context, buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout, ImageLoaderMock.mock()));
778 789
             }
779 790
         });
780 791
     }

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

@@ -6,6 +6,7 @@ import android.view.ViewGroup;
6 6
 
7 7
 import com.reactnativenavigation.BaseTest;
8 8
 import com.reactnativenavigation.R;
9
+import com.reactnativenavigation.mocks.ImageLoaderMock;
9 10
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
10 11
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
11 12
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
@@ -41,7 +42,7 @@ public class TopBarBackgroundComponentTest extends BaseTest {
41 42
         Activity activity = newActivity();
42 43
         topBarBackgroundViewController = spy(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()));
43 44
         StackLayout parent = new StackLayout(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), topBarBackgroundViewController, new TopBarController(), onClickListener, null);
44
-        uut = new TopBar(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), topBarBackgroundViewController, onClickListener, parent);
45
+        uut = new TopBar(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), topBarBackgroundViewController, onClickListener, parent, ImageLoaderMock.mock());
45 46
         parent.addView(uut);
46 47
     }
47 48
 

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

@@ -6,6 +6,7 @@ import android.view.MenuItem;
6 6
 
7 7
 import com.reactnativenavigation.BaseTest;
8 8
 import com.reactnativenavigation.anim.TopBarAnimator;
9
+import com.reactnativenavigation.mocks.ImageLoaderMock;
9 10
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
10 11
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
11 12
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
@@ -49,7 +50,7 @@ public class TopBarTest extends BaseTest {
49 50
         Activity activity = newActivity();
50 51
         TopBarBackgroundViewController topBarBackgroundViewController = new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock());
51 52
         StackLayout parent = new StackLayout(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), topBarBackgroundViewController, new TopBarController(), this.onClickListener, null);
52
-        uut = new TopBar(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), topBarBackgroundViewController, this.onClickListener, parent);
53
+        uut = new TopBar(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), topBarBackgroundViewController, this.onClickListener, parent, ImageLoaderMock.mock());
53 54
         animator = spy(new TopBarAnimator(uut));
54 55
         uut.setAnimator(animator);
55 56
         leftButton = createLeftButton();

+ 11
- 0
lib/ios/RNNBackButtonOptions.h View File

@@ -0,0 +1,11 @@
1
+#import "RNNOptions.h"
2
+
3
+@interface RNNBackButtonOptions : RNNOptions
4
+
5
+@property (nonatomic, strong) NSDictionary* image;
6
+@property (nonatomic, strong) NSNumber* visible;
7
+@property (nonatomic, strong) NSString* title;
8
+@property (nonatomic, strong) NSString* transition;
9
+@property (nonatomic, strong) NSNumber* hideTitle;
10
+
11
+@end

+ 26
- 0
lib/ios/RNNBackButtonOptions.m View File

@@ -0,0 +1,26 @@
1
+#import "RNNBackButtonOptions.h"
2
+
3
+@implementation RNNBackButtonOptions
4
+
5
+- (void)applyOn:(UIViewController *)viewController {
6
+	UIImage *image = self.image ? [RCTConvert UIImage:self.image] : nil;
7
+	[viewController.navigationController.navigationBar setBackIndicatorImage:image];
8
+	[viewController.navigationController.navigationBar setBackIndicatorTransitionMaskImage:image];
9
+	
10
+	if ([self.hideTitle boolValue]) {
11
+		self.title = @"";
12
+	}
13
+	
14
+	if (self.title) {
15
+		UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:self.title
16
+																	 style:UIBarButtonItemStylePlain
17
+																	target:nil
18
+																	action:nil];
19
+		
20
+		viewController.navigationItem.backBarButtonItem = backItem;
21
+	}
22
+	
23
+	viewController.navigationItem.hidesBackButton = ![self.visible boolValue];
24
+}
25
+
26
+@end

+ 0
- 1
lib/ios/RNNNavigationOptions.h View File

@@ -31,7 +31,6 @@ extern const NSInteger TOP_BAR_TRANSPARENT_TAG;
31 31
 @property (nonatomic, strong) RNNLayoutOptions* layout;
32 32
 
33 33
 @property (nonatomic, strong) NSMutableDictionary* originalTopBarImages;
34
-@property (nonatomic, strong) NSString* backButtonTransition;
35 34
 @property (nonatomic, strong) NSNumber* popGesture;
36 35
 @property (nonatomic, strong) NSDictionary* backgroundImage;
37 36
 @property (nonatomic, strong) NSDictionary* rootBackgroundImage;

+ 1
- 1
lib/ios/RNNRootViewController.m View File

@@ -225,7 +225,7 @@
225 225
 
226 226
 - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
227 227
 	RNNRootViewController* vc =  (RNNRootViewController*)viewController;
228
-	if (![vc.options.backButtonTransition isEqualToString:@"custom"]){
228
+	if (![vc.options.topBar.backButton.transition isEqualToString:@"custom"]){
229 229
 		navigationController.delegate = nil;
230 230
 	}
231 231
 }

+ 2
- 4
lib/ios/RNNTopBarOptions.h View File

@@ -4,6 +4,7 @@
4 4
 #import "RNNSubtitleOptions.h"
5 5
 #import "RNNBackgroundOptions.h"
6 6
 #import "RNNComponentOptions.h"
7
+#import "RNNBackButtonOptions.h"
7 8
 
8 9
 @interface RNNTopBarOptions : RNNOptions
9 10
 
@@ -23,10 +24,7 @@
23 24
 @property (nonatomic, strong) RNNTitleOptions* title;
24 25
 @property (nonatomic, strong) RNNSubtitleOptions* subtitle;
25 26
 @property (nonatomic, strong) RNNBackgroundOptions* background;
26
-@property (nonatomic, strong) NSNumber* backButtonImage;
27
-@property (nonatomic, strong) NSNumber* backButtonHidden;
28
-@property (nonatomic, strong) NSString* backButtonTitle;
29
-@property (nonatomic, strong) NSNumber* hideBackButtonTitle;
27
+@property (nonatomic, strong) RNNBackButtonOptions* backButton;
30 28
 @property (nonatomic, strong) NSNumber* searchBar;
31 29
 @property (nonatomic, strong) NSNumber* searchBarHiddenWhenScrolling;
32 30
 @property (nonatomic, strong) NSString* searchBarPlaceholder;

+ 8
- 7
lib/ios/RNNTopBarOptions.m View File

@@ -30,6 +30,7 @@ extern const NSInteger BLUR_TOPBAR_TAG;
30 30
 	[self.title applyOn:viewController];
31 31
 	[self.largeTitle applyOn:viewController];
32 32
 	[self.background applyOn:viewController];
33
+	[self.backButton applyOn:viewController];
33 34
 	
34 35
 	if (@available(iOS 11.0, *)) {
35 36
 		if ([self.searchBar boolValue] && !viewController.navigationItem.searchController) {
@@ -149,17 +150,17 @@ extern const NSInteger BLUR_TOPBAR_TAG;
149 150
 		_navigationButtons = [[RNNNavigationButtons alloc] initWithViewController:(RNNRootViewController*)viewController];
150 151
 		[_navigationButtons applyLeftButtons:self.leftButtons rightButtons:self.rightButtons];
151 152
 	}
152
-	
153
-	UIImage *image = self.backButtonImage ? [RCTConvert UIImage:self.backButtonImage] : nil;
153
+
154
+	UIImage *image = self.backButton.image ? [RCTConvert UIImage:self.backButton.image] : nil;
154 155
 	[viewController.navigationController.navigationBar setBackIndicatorImage:image];
155 156
 	[viewController.navigationController.navigationBar setBackIndicatorTransitionMaskImage:image];
156 157
 	
157
-	if (self.hideBackButtonTitle) {
158
-		self.backButtonTitle = @"";
158
+	if (![self.backButton.visible boolValue]) {
159
+		self.backButton.title = @"";
159 160
 	}
160 161
 	
161
-	if (self.backButtonTitle) {
162
-		UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:self.backButtonTitle
162
+	if (self.backButton.title) {
163
+		UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:self.backButton.title
163 164
 																	 style:UIBarButtonItemStylePlain
164 165
 																	target:nil
165 166
 																	action:nil];
@@ -167,7 +168,7 @@ extern const NSInteger BLUR_TOPBAR_TAG;
167 168
 		viewController.navigationItem.backBarButtonItem = backItem;
168 169
 	}
169 170
 	
170
-	viewController.navigationItem.hidesBackButton = [self.backButtonHidden boolValue];
171
+	viewController.navigationItem.hidesBackButton = ![self.backButton.visible boolValue];
171 172
 }
172 173
 
173 174
 -(void)storeOriginalTopBarImages:(UIViewController*)viewController {

+ 8
- 0
lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj View File

@@ -68,6 +68,8 @@
68 68
 		50175CD1207A2AA1004FE91B /* RNNComponentOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 50175CCF207A2AA1004FE91B /* RNNComponentOptions.h */; };
69 69
 		50175CD2207A2AA1004FE91B /* RNNComponentOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 50175CD0207A2AA1004FE91B /* RNNComponentOptions.m */; };
70 70
 		502CB43A20CBCA180019B2FE /* RNNBridgeManagerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 502CB43920CBCA140019B2FE /* RNNBridgeManagerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
71
+		502CB46E20CD1DDA0019B2FE /* RNNBackButtonOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 502CB46C20CD1DDA0019B2FE /* RNNBackButtonOptions.h */; };
72
+		502CB46F20CD1DDA0019B2FE /* RNNBackButtonOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 502CB46D20CD1DDA0019B2FE /* RNNBackButtonOptions.m */; };
71 73
 		50415CBA20553B8E00BB682E /* RNNScreenTransition.h in Headers */ = {isa = PBXBuildFile; fileRef = 50415CB820553B8E00BB682E /* RNNScreenTransition.h */; };
72 74
 		50415CBB20553B8E00BB682E /* RNNScreenTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 50415CB920553B8E00BB682E /* RNNScreenTransition.m */; };
73 75
 		50451D052042DAEB00695F00 /* RNNPushAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 50451D032042DAEB00695F00 /* RNNPushAnimation.h */; };
@@ -279,6 +281,8 @@
279 281
 		50175CCF207A2AA1004FE91B /* RNNComponentOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNComponentOptions.h; sourceTree = "<group>"; };
280 282
 		50175CD0207A2AA1004FE91B /* RNNComponentOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNComponentOptions.m; sourceTree = "<group>"; };
281 283
 		502CB43920CBCA140019B2FE /* RNNBridgeManagerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNBridgeManagerDelegate.h; sourceTree = "<group>"; };
284
+		502CB46C20CD1DDA0019B2FE /* RNNBackButtonOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNBackButtonOptions.h; sourceTree = "<group>"; };
285
+		502CB46D20CD1DDA0019B2FE /* RNNBackButtonOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNBackButtonOptions.m; sourceTree = "<group>"; };
282 286
 		50415CB820553B8E00BB682E /* RNNScreenTransition.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNScreenTransition.h; sourceTree = "<group>"; };
283 287
 		50415CB920553B8E00BB682E /* RNNScreenTransition.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNScreenTransition.m; sourceTree = "<group>"; };
284 288
 		50451D032042DAEB00695F00 /* RNNPushAnimation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNPushAnimation.h; sourceTree = "<group>"; };
@@ -538,6 +542,8 @@
538 542
 				50175CD0207A2AA1004FE91B /* RNNComponentOptions.m */,
539 543
 				A7626BFE1FC2FB6700492FB8 /* RNNTopBarOptions.h */,
540 544
 				A7626BFC1FC2FB2C00492FB8 /* RNNTopBarOptions.m */,
545
+				502CB46C20CD1DDA0019B2FE /* RNNBackButtonOptions.h */,
546
+				502CB46D20CD1DDA0019B2FE /* RNNBackButtonOptions.m */,
541 547
 				50570B242061473D006A1B5C /* RNNTitleOptions.h */,
542 548
 				50570B252061473D006A1B5C /* RNNTitleOptions.m */,
543 549
 				50C4A494206BDDBB00DB292E /* RNNSubtitleOptions.h */,
@@ -807,6 +813,7 @@
807 813
 				50451D052042DAEB00695F00 /* RNNPushAnimation.h in Headers */,
808 814
 				507F43C51FF4F17C00D9425B /* RNNTopTabsViewController.h in Headers */,
809 815
 				263905C61E4C6F440023D7D3 /* SidebarFeedlyAnimation.h in Headers */,
816
+				502CB46E20CD1DDA0019B2FE /* RNNBackButtonOptions.h in Headers */,
810 817
 				7B1126A31E2D2B6C00F9B03B /* RNNSplashScreen.h in Headers */,
811 818
 				261F0E641E6EC94900989DE2 /* RNNModalManager.h in Headers */,
812 819
 				263905C01E4C6F440023D7D3 /* SidebarAirbnbAnimation.h in Headers */,
@@ -968,6 +975,7 @@
968 975
 				263905C71E4C6F440023D7D3 /* SidebarFeedlyAnimation.m in Sources */,
969 976
 				50C4A497206BDDBB00DB292E /* RNNSubtitleOptions.m in Sources */,
970 977
 				263905B41E4C6F440023D7D3 /* MMDrawerVisualState.m in Sources */,
978
+				502CB46F20CD1DDA0019B2FE /* RNNBackButtonOptions.m in Sources */,
971 979
 				263905C51E4C6F440023D7D3 /* SidebarFacebookAnimation.m in Sources */,
972 980
 				50451D0E2042F70900695F00 /* RNNTransition.m in Sources */,
973 981
 				5048862E20BE976D000908DE /* RNNLayoutOptions.m in Sources */,

+ 3
- 1
playground/src/screens/CustomTransitionDestination.js View File

@@ -12,12 +12,14 @@ class CustomTransitionDestination extends Component {
12 12
 
13 13
   static get options() {
14 14
     return {
15
-      backButtonTransition: 'custom',
16 15
       topBar: {
17 16
         title: {
18 17
           text: 'ye babyyyyyy',
19 18
           fontFamily: 'HelveticaNeue-Italic'
20 19
         },
20
+        backButton: {
21
+          transition: 'custom'
22
+        },
21 23
         largeTitle: {
22 24
           visible: false
23 25
         }