Просмотр исходного кода

[BREAKING] BottomTabs refactor (#3469)

* Refactored bottomTab and bottomTabs

* Initial commit before switching to our fork of AHBottomNavigation

* Bump AHBottomNavigation version

* More work on BottomTab options

Need to merge some child options when a child is attached to parent

* Fix tests

* DefaultOptions refactor

* defaultOptions are now merged before applying options
* Handle BottomTab options in dedicated presenter
* Create BottomTabs in BottomTabsController.createView() instead of constructor

* Set bottomTab TypeFace

* Update Android installation guide

* fixed unit tests

* Fix unit tests

Kind of a workaround

* Handle NPE in getPreferredHeight

If a view hasn't been attached yet, return 0 height

* Apply BottomTab text and icon color individually

wix-playground/AHBottomNavigation does not support setting color for the entire tab (icon and text),
instead it supports setting color individually to text and icon. Another change is that it no longer supports
default color for icons. If icon color is undefined - it won't tint the icon.

* Add getCurrentChild to ParentController contract

* Clear backButton when setting stack root

* Add  ViewController.resolveCurrentOptions

resolveCurrentOptions merges the controllers options with all of its current children

* StackController refactor

* Create pushed child view only in StackController.createView
* When initialising a stack with multiple children, only create the top child's view
* StackController constructor now accepts children
* When pushing child to stack, apply options that change LayoutParams

* Apply BottomTabs layout options when tab views are created

* bototmTab changes in iOS

* options fix

* Fixes options propagating
Guy Carmeli 6 лет назад
Родитель
Сommit
2c33830f3e
63 измененных файлов: 1090 добавлений и 395 удалений
  1. 1
    0
      docs/docs/Installing.md
  2. 8
    6
      docs/docs/styling.md
  3. 5
    1
      lib/android/app/build.gradle
  4. 39
    27
      lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabOptions.java
  5. 0
    8
      lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabsOptions.java
  6. 37
    24
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java
  7. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutNode.java
  8. 4
    9
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java
  9. 14
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/OrientationOptions.java
  10. 72
    0
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabOptionsPresenter.java
  11. 34
    25
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsOptionsPresenter.java
  12. 23
    9
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java
  13. 39
    14
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackOptionsPresenter.java
  14. 1
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ViewUtils.java
  15. 5
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ChildController.java
  16. 3
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java
  17. 8
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java
  18. 23
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java
  19. 13
    3
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/SideMenuController.java
  20. 10
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java
  21. 4
    4
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabFinder.java
  22. 31
    18
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java
  23. 8
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/BackButtonHelper.java
  24. 52
    14
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java
  25. 39
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java
  26. 7
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java
  27. 2
    12
      lib/android/app/src/main/java/com/reactnativenavigation/views/BottomTabs.java
  28. 9
    3
      lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentLayout.java
  29. 0
    21
      lib/android/app/src/main/java/com/reactnativenavigation/views/StackLayout.java
  30. 17
    1
      lib/android/app/src/test/java/com/reactnativenavigation/TestUtils.java
  31. 2
    1
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleComponentViewController.java
  32. 15
    3
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleViewController.java
  33. 2
    2
      lib/android/app/src/test/java/com/reactnativenavigation/parse/OptionsTest.java
  34. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/utils/OptionHelper.java
  35. 96
    0
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabOptionsPresenterTest.java
  36. 67
    26
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsControllerTest.java
  37. 3
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ComponentViewControllerTest.java
  38. 1
    0
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/FloatingActionButtonTest.java
  39. 64
    28
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java
  40. 7
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java
  41. 8
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ParentControllerTest.java
  42. 3
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/SideMenuControllerTest.java
  43. 22
    16
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackOptionsPresenterTest.java
  44. 9
    3
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java
  45. 1
    0
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ViewControllerTest.java
  46. 9
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/BackButtonHelperTest.java
  47. 78
    13
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.java
  48. 8
    3
      lib/ios/RNNBottomTabOptions.h
  49. 83
    23
      lib/ios/RNNBottomTabOptions.m
  50. 2
    2
      lib/ios/RNNBottomTabsOptions.h
  51. 1
    18
      lib/ios/RNNBottomTabsOptions.m
  52. 3
    2
      lib/ios/RNNControllerFactory.m
  53. 2
    0
      lib/ios/RNNNavigationOptions.h
  54. 1
    0
      lib/ios/RNNNavigationOptions.m
  55. 1
    0
      lib/ios/RNNUtils.h
  56. 8
    0
      lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj
  57. 12
    23
      lib/ios/ReactNativeNavigationTests/RNNRootViewControllerTest.m
  58. 7
    0
      lib/ios/UIImage+tint.h
  59. 29
    0
      lib/ios/UIImage+tint.m
  60. 1
    0
      playground/android/build.gradle
  61. 8
    0
      playground/src/app.js
  62. 3
    5
      playground/src/screens/TextScreen.js
  63. 24
    13
      playground/src/screens/WelcomeScreen.js

+ 1
- 0
docs/docs/Installing.md Просмотреть файл

@@ -80,6 +80,7 @@
80 80
 	           // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
81 81
 	           url "$rootDir/../node_modules/react-native/android"
82 82
 			}
83
+  +		maven { url 'https://jitpack.io' }
83 84
 		}
84 85
 	}
85 86
 	```

+ 8
- 6
docs/docs/styling.md Просмотреть файл

@@ -120,17 +120,19 @@ Navigation.mergeOptions(this.props.componentId, {
120 120
     currentTabId: 'currentTabId',
121 121
     testID: 'bottomTabsTestID',
122 122
     drawBehind: false,
123
-    backgroundColor: 'white',
124
-    tabColor: 'red',
125
-    selectedTabColor: 'blue',
126
-    fontFamily: 'Helvetica',
127
-    fontSize: 10
123
+    backgroundColor: 'white'
128 124
   },
129 125
   bottomTab: {
130 126
     title: 'Tab 1',
131 127
     badge: '2',
132 128
     testID: 'bottomTabTestID',
133
-    icon: require('tab.png')
129
+    icon: require('tab.png'),
130
+    iconColor: 'red',
131
+    selectedIconColor: 'blue',
132
+    textColor: 'red',
133
+    selectedTextColor: 'blue',
134
+    fontFamily: 'Helvetica',
135
+    fontSize: 10
134 136
   },
135 137
   sideMenu: {
136 138
     left: {

+ 5
- 1
lib/android/app/build.gradle Просмотреть файл

@@ -62,6 +62,9 @@ allprojects { p ->
62 62
         p.android.compileOptions.sourceCompatibility JavaVersion.VERSION_1_8
63 63
         p.android.compileOptions.targetCompatibility JavaVersion.VERSION_1_8
64 64
     }
65
+    p.repositories {
66
+        maven { url "https://jitpack.io" }
67
+    }
65 68
 }
66 69
 
67 70
 dependencies {
@@ -69,7 +72,8 @@ dependencies {
69 72
     implementation 'com.android.support:design:26.1.0'
70 73
     implementation 'com.android.support:appcompat-v7:26.1.0'
71 74
     implementation 'com.android.support:support-v4:26.1.0'
72
-    implementation 'com.aurelhubert:ahbottomnavigation:2.1.0'
75
+    implementation 'com.github.wix-playground:ahbottomnavigation:2.4.3'
76
+
73 77
     implementation 'com.github.clans:fab:1.6.4'
74 78
 
75 79
     //noinspection GradleDynamicVersion

+ 39
- 27
lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabOptions.java Просмотреть файл

@@ -1,55 +1,67 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3
+import android.graphics.Typeface;
4
+import android.support.annotation.Nullable;
5
+
6
+import com.reactnativenavigation.parse.params.Color;
7
+import com.reactnativenavigation.parse.params.NullColor;
3 8
 import com.reactnativenavigation.parse.params.NullText;
4 9
 import com.reactnativenavigation.parse.params.Text;
10
+import com.reactnativenavigation.parse.parsers.ColorParser;
5 11
 import com.reactnativenavigation.parse.parsers.TextParser;
12
+import com.reactnativenavigation.utils.TypefaceLoader;
6 13
 
7 14
 import org.json.JSONObject;
8 15
 
9 16
 public class BottomTabOptions {
10 17
 
11
-    public static BottomTabOptions parse(JSONObject json) {
18
+    public static BottomTabOptions parse(TypefaceLoader typefaceManager, JSONObject json) {
12 19
         BottomTabOptions options = new BottomTabOptions();
13 20
         if (json == null) return options;
14 21
 
15
-        options.title = TextParser.parse(json, "title");
16
-        if (json.has("icon")) {
17
-            options.icon = TextParser.parse(json.optJSONObject("icon"), "uri");
18
-        }
22
+        options.text = TextParser.parse(json, "text");
23
+        options.textColor = ColorParser.parse(json, "textColor");
24
+        options.selectedTextColor = ColorParser.parse(json, "selectedTextColor");
25
+        if (json.has("icon")) options.icon = TextParser.parse(json.optJSONObject("icon"), "uri");
26
+        options.iconColor = ColorParser.parse(json, "iconColor");
27
+        options.selectedIconColor = ColorParser.parse(json, "selectedIconColor");
19 28
         options.badge = TextParser.parse(json, "badge");
20 29
         options.testId = TextParser.parse(json, "testID");
30
+        options.fontFamily = typefaceManager.getTypeFace(json.optString("fontFamily", ""));
21 31
         return options;
22 32
     }
23 33
 
24
-    public Text title = new NullText();
34
+    public Text text = new NullText();
35
+    public Color textColor = new NullColor();
36
+    public Color selectedTextColor = new NullColor();
25 37
     public Text icon = new NullText();
38
+    public Color iconColor = new NullColor();
39
+    public Color selectedIconColor = new NullColor();
26 40
     public Text testId = new NullText();
27 41
     public Text badge = new NullText();
42
+    @Nullable public Typeface fontFamily;
43
+
28 44
 
29 45
     void mergeWith(final BottomTabOptions other) {
30
-        if (other.title.hasValue()) {
31
-            title = other.title;
32
-        }
33
-        if (other.icon.hasValue()) {
34
-            icon = other.icon;
35
-        }
36
-        if (other.badge.hasValue()) {
37
-            badge = other.badge;
38
-        }
39
-        if (other.testId.hasValue()) {
40
-            testId = other.testId;
41
-        }
46
+        if (other.text.hasValue()) text = other.text;
47
+        if (other.textColor.hasValue()) textColor = other.textColor;
48
+        if (other.selectedTextColor.hasValue()) selectedTextColor = other.selectedTextColor;
49
+        if (other.icon.hasValue()) icon = other.icon;
50
+        if (other.iconColor.hasValue()) iconColor = other.iconColor;
51
+        if (other.selectedIconColor.hasValue()) selectedIconColor = other.selectedIconColor;
52
+        if (other.badge.hasValue()) badge = other.badge;
53
+        if (other.testId.hasValue()) testId = other.testId;
54
+        if (other.fontFamily != null) fontFamily = other.fontFamily;
42 55
     }
43 56
 
44 57
     void mergeWithDefault(final BottomTabOptions defaultOptions) {
45
-        if (!title.hasValue()) {
46
-            title = defaultOptions.title;
47
-        }
48
-        if (!icon.hasValue()) {
49
-            icon = defaultOptions.icon;
50
-        }
51
-        if (!badge.hasValue()) {
52
-            badge = defaultOptions.badge;
53
-        }
58
+        if (!text.hasValue()) text = defaultOptions.text;
59
+        if (!textColor.hasValue()) textColor = defaultOptions.textColor;
60
+        if (!selectedTextColor.hasValue()) selectedTextColor = defaultOptions.selectedTextColor;
61
+        if (!icon.hasValue()) icon = defaultOptions.icon;
62
+        if (!iconColor.hasValue()) iconColor = defaultOptions.iconColor;
63
+        if (!selectedIconColor.hasValue()) selectedIconColor = defaultOptions.selectedIconColor;
64
+        if (!badge.hasValue()) badge = defaultOptions.badge;
65
+        if (fontFamily == null) fontFamily = defaultOptions.fontFamily;
54 66
     }
55 67
 }

+ 0
- 8
lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabsOptions.java Просмотреть файл

@@ -23,8 +23,6 @@ public class BottomTabsOptions {
23 23
 		if (json == null) return options;
24 24
 
25 25
         options.backgroundColor = ColorParser.parse(json, "backgroundColor");
26
-        options.tabColor = ColorParser.parse(json, "tabColor");
27
-        options.selectedTabColor = ColorParser.parse(json, "selectedTabColor");
28 26
         options.currentTabId = TextParser.parse(json, "currentTabId");
29 27
 		options.currentTabIndex = NumberParser.parse(json,"currentTabIndex");
30 28
 		options.visible = BoolParser.parse(json,"visible");
@@ -37,8 +35,6 @@ public class BottomTabsOptions {
37 35
 	}
38 36
 
39 37
     public Color backgroundColor = new NullColor();
40
-    public Color tabColor = new NullColor();
41
-    public Color selectedTabColor = new NullColor();
42 38
 	public Bool visible = new NullBool();
43 39
     public Bool drawBehind = new NullBool();
44 40
 	public Bool animate = new NullBool();
@@ -53,8 +49,6 @@ public class BottomTabsOptions {
53 49
 		if (other.visible.hasValue()) visible = other.visible;
54 50
         if (other.drawBehind.hasValue()) drawBehind = other.drawBehind;
55 51
 		if (other.animate.hasValue()) animate = other.animate;
56
-        if (other.tabColor.hasValue()) tabColor = other.tabColor;
57
-        if (other.selectedTabColor.hasValue()) selectedTabColor = other.selectedTabColor;
58 52
         if (other.backgroundColor.hasValue()) backgroundColor = other.backgroundColor;
59 53
         if (other.testId.hasValue()) testId = other.testId;
60 54
         if (other.titleDisplayMode.hasValue()) titleDisplayMode = other.titleDisplayMode;
@@ -66,8 +60,6 @@ public class BottomTabsOptions {
66 60
         if (!visible.hasValue()) visible = defaultOptions.visible;
67 61
         if (!drawBehind.hasValue()) drawBehind = defaultOptions.drawBehind;
68 62
         if (!animate.hasValue()) animate = defaultOptions.animate;
69
-        if (!tabColor.hasValue()) tabColor = defaultOptions.tabColor;
70
-        if (!selectedTabColor.hasValue()) selectedTabColor = defaultOptions.selectedTabColor;
71 63
         if (!backgroundColor.hasValue()) backgroundColor = defaultOptions.backgroundColor;
72 64
         if (!titleDisplayMode.hasValue()) titleDisplayMode = defaultOptions.titleDisplayMode;
73 65
     }

+ 37
- 24
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java Просмотреть файл

@@ -3,19 +3,21 @@ package com.reactnativenavigation.parse;
3 3
 import android.app.Activity;
4 4
 
5 5
 import com.facebook.react.ReactInstanceManager;
6
+import com.reactnativenavigation.presentation.BottomTabOptionsPresenter;
7
+import com.reactnativenavigation.presentation.BottomTabsOptionsPresenter;
8
+import com.reactnativenavigation.presentation.OptionsPresenter;
9
+import com.reactnativenavigation.presentation.StackOptionsPresenter;
6 10
 import com.reactnativenavigation.react.EventEmitter;
7
-import com.reactnativenavigation.utils.CommandListenerAdapter;
8 11
 import com.reactnativenavigation.utils.ImageLoader;
9 12
 import com.reactnativenavigation.utils.TypefaceLoader;
10 13
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
11 14
 import com.reactnativenavigation.viewcontrollers.ComponentViewController;
12 15
 import com.reactnativenavigation.viewcontrollers.SideMenuController;
13
-import com.reactnativenavigation.viewcontrollers.stack.StackController;
14
-import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
15 16
 import com.reactnativenavigation.viewcontrollers.ViewController;
16 17
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabsController;
17 18
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentCreator;
18 19
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentViewController;
20
+import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
19 21
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
20 22
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
21 23
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
@@ -29,6 +31,8 @@ import java.util.ArrayList;
29 31
 import java.util.List;
30 32
 import java.util.Map;
31 33
 
34
+import static com.reactnativenavigation.parse.Options.parse;
35
+
32 36
 public class LayoutFactory {
33 37
 
34 38
 	private final Activity activity;
@@ -75,7 +79,12 @@ public class LayoutFactory {
75 79
 	}
76 80
 
77 81
     private ViewController createSideMenuRoot(LayoutNode node) {
78
-		SideMenuController sideMenuController = new SideMenuController(activity, childRegistry, node.id, parseNodeOptions(node));
82
+		SideMenuController sideMenuController = new SideMenuController(activity,
83
+                childRegistry,
84
+                node.id,
85
+                parse(typefaceManager, node.getOptions()),
86
+                new OptionsPresenter(activity, defaultOptions)
87
+        );
79 88
 		ViewController childControllerCenter = null, childControllerLeft = null, childControllerRight = null;
80 89
 
81 90
 		for (LayoutNode child : node.children) {
@@ -97,11 +106,6 @@ public class LayoutFactory {
97 106
 			}
98 107
 		}
99 108
 
100
-		// Need to set the center controller first, otherwise "onPress" events on the JS components
101
-		// of the left and right drawers are not handled properly.
102
-		//
103
-		// See https://github.com/wix/react-native-navigation/issues/2835
104
-		//
105 109
 		if (childControllerCenter != null) {
106 110
 			sideMenuController.setCenterController(childControllerCenter);
107 111
 		}
@@ -137,7 +141,8 @@ public class LayoutFactory {
137 141
                 id,
138 142
                 name,
139 143
                 new ComponentViewCreator(reactInstanceManager),
140
-                parseNodeOptions(node)
144
+                parse(typefaceManager, node.getOptions()),
145
+                new OptionsPresenter(activity, defaultOptions)
141 146
         );
142 147
 	}
143 148
 
@@ -148,28 +153,31 @@ public class LayoutFactory {
148 153
                 externalComponent,
149 154
                 externalComponentCreators.get(externalComponent.name.get()),
150 155
                 reactInstanceManager,
151
-                parseNodeOptions(node)
156
+                parse(typefaceManager, node.getOptions())
152 157
         );
153 158
     }
154 159
 
155 160
 	private ViewController createStack(LayoutNode node) {
156
-        StackController stackController = new StackControllerBuilder(activity)
161
+        return new StackControllerBuilder(activity)
162
+                .setChildren(createChildredn(node.children))
157 163
                 .setChildRegistry(childRegistry)
158 164
                 .setTopBarButtonCreator(new TitleBarButtonCreator(reactInstanceManager))
159 165
                 .setTitleBarReactViewCreator(new TitleBarReactViewCreator(reactInstanceManager))
160 166
                 .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreator(reactInstanceManager)))
161 167
                 .setTopBarController(new TopBarController())
162 168
                 .setId(node.id)
163
-                .setInitialOptions(parseNodeOptions(node))
169
+                .setInitialOptions(parse(typefaceManager, node.getOptions()))
170
+                .setOptionsPresenter(new OptionsPresenter(activity, defaultOptions))
171
+                .setStackPresenter(new StackOptionsPresenter(activity, defaultOptions))
164 172
                 .build();
165
-        addChildrenToStack(node.children, stackController);
166
-        return stackController;
167 173
 	}
168 174
 
169
-    private void addChildrenToStack(List<LayoutNode> children, StackController stackController) {
175
+    private List<ViewController> createChildredn(List<LayoutNode> children) {
176
+        List<ViewController> result = new ArrayList<>();
170 177
         for (LayoutNode child : children) {
171
-            stackController.push(create(child), new CommandListenerAdapter());
178
+            result.add(create(child));
172 179
         }
180
+        return result;
173 181
     }
174 182
 
175 183
     private ViewController createBottomTabs(LayoutNode node) {
@@ -177,21 +185,26 @@ public class LayoutFactory {
177 185
         for (int i = 0; i < node.children.size(); i++) {
178 186
             tabs.add(create(node.children.get(i)));
179 187
         }
180
-        return new BottomTabsController(activity, tabs, childRegistry, eventEmitter, new ImageLoader(), node.id, parseNodeOptions(node));
188
+        return new BottomTabsController(activity,
189
+                tabs,
190
+                childRegistry,
191
+                eventEmitter,
192
+                new ImageLoader(),
193
+                node.id,
194
+                parse(typefaceManager, node.getOptions()),
195
+                new OptionsPresenter(activity, defaultOptions),
196
+                new BottomTabsOptionsPresenter(tabs, defaultOptions),
197
+                new BottomTabOptionsPresenter(activity, tabs, defaultOptions));
181 198
 	}
182 199
 
183 200
     private ViewController createTopTabs(LayoutNode node) {
184 201
         final List<ViewController> tabs = new ArrayList<>();
185 202
         for (int i = 0; i < node.children.size(); i++) {
186 203
             ViewController tabController = create(node.children.get(i));
187
-            Options options = parseNodeOptions(node.children.get(i));
204
+            Options options = parse(typefaceManager, node.children.get(i).getOptions());
188 205
             options.setTopTabIndex(i);
189 206
             tabs.add(tabController);
190 207
         }
191
-        return new TopTabsController(activity, childRegistry, node.id, tabs, new TopTabsLayoutCreator(activity, tabs), parseNodeOptions(node));
192
-    }
193
-
194
-    private Options parseNodeOptions(LayoutNode node) {
195
-        return Options.parse(typefaceManager, node.getNavigationOptions(), defaultOptions);
208
+        return new TopTabsController(activity, childRegistry, node.id, tabs, new TopTabsLayoutCreator(activity, tabs), parse(typefaceManager, node.getOptions()), new OptionsPresenter(activity, defaultOptions));
196 209
     }
197 210
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutNode.java Просмотреть файл

@@ -35,7 +35,7 @@ public class LayoutNode {
35 35
 		this.children = children;
36 36
 	}
37 37
 
38
-    JSONObject getNavigationOptions() {
38
+    JSONObject getOptions() {
39 39
 	    return data.optJSONObject("options");
40 40
     }
41 41
 }

+ 4
- 9
lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java Просмотреть файл

@@ -13,18 +13,13 @@ public class Options {
13 13
 
14 14
     @NonNull
15 15
     public static Options parse(TypefaceLoader typefaceManager, JSONObject json) {
16
-        return parse(typefaceManager, json, new Options());
17
-    }
18
-
19
-    @NonNull
20
-    public static Options parse(TypefaceLoader typefaceManager, JSONObject json, @NonNull Options defaultOptions) {
21 16
         Options result = new Options();
22
-        if (json == null) return result.withDefaultOptions(defaultOptions);
17
+        if (json == null) return result;
23 18
 
24 19
         result.topBar = TopBarOptions.parse(typefaceManager, json.optJSONObject("topBar"));
25 20
         result.topTabs = TopTabsOptions.parse(json.optJSONObject("topTabs"));
26 21
         result.topTabOptions = TopTabOptions.parse(typefaceManager, json.optJSONObject("topTab"));
27
-        result.bottomTabOptions = BottomTabOptions.parse(json.optJSONObject("bottomTab"));
22
+        result.bottomTabOptions = BottomTabOptions.parse(typefaceManager, json.optJSONObject("bottomTab"));
28 23
         result.bottomTabsOptions = BottomTabsOptions.parse(json.optJSONObject("bottomTabs"));
29 24
         result.overlayOptions = OverlayOptions.parse(json.optJSONObject("overlay"));
30 25
         result.fabOptions = FabOptions.parse(json.optJSONObject("fab"));
@@ -34,7 +29,7 @@ public class Options {
34 29
         result.statusBar = StatusBarOptions.parse(json.optJSONObject("statusBar"));
35 30
         result.layout = LayoutOptions.parse(json.optJSONObject("layout"));
36 31
 
37
-        return result.withDefaultOptions(defaultOptions);
32
+        return result;
38 33
     }
39 34
 
40 35
     @NonNull public TopBarOptions topBar = new TopBarOptions();
@@ -89,7 +84,7 @@ public class Options {
89 84
         return result;
90 85
     }
91 86
 
92
-    Options withDefaultOptions(final Options defaultOptions) {
87
+    public Options withDefaultOptions(final Options defaultOptions) {
93 88
         topBar.mergeWithDefault(defaultOptions.topBar);
94 89
         topTabOptions.mergeWithDefault(defaultOptions.topTabOptions);
95 90
         topTabs.mergeWithDefault(defaultOptions.topTabs);

+ 14
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/OrientationOptions.java Просмотреть файл

@@ -1,5 +1,7 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3
+import android.support.annotation.CheckResult;
4
+
3 5
 import com.reactnativenavigation.parse.params.Orientation;
4 6
 
5 7
 import org.json.JSONArray;
@@ -52,6 +54,18 @@ public class OrientationOptions {
52 54
         return !orientations.isEmpty();
53 55
     }
54 56
 
57
+    @CheckResult
58
+    public OrientationOptions copy() {
59
+        OrientationOptions result = new OrientationOptions();
60
+        result.orientations = new ArrayList<>(orientations);
61
+        return result;
62
+    }
63
+
64
+    public OrientationOptions mergeWithDefault(OrientationOptions defaultOptions) {
65
+        if (!hasValue()) orientations = defaultOptions.orientations;
66
+        return this;
67
+    }
68
+
55 69
     @Override
56 70
     public String toString() {
57 71
         return hasValue() ? Arrays.toString(orientations.toArray(new Orientation[0])) : Orientation.Default.toString();

+ 72
- 0
lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabOptionsPresenter.java Просмотреть файл

@@ -0,0 +1,72 @@
1
+package com.reactnativenavigation.presentation;
2
+
3
+import android.content.Context;
4
+import android.support.annotation.VisibleForTesting;
5
+import android.support.v4.content.ContextCompat;
6
+
7
+import com.reactnativenavigation.parse.BottomTabOptions;
8
+import com.reactnativenavigation.parse.Options;
9
+import com.reactnativenavigation.viewcontrollers.ViewController;
10
+import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabFinder;
11
+import com.reactnativenavigation.views.BottomTabs;
12
+import com.reactnativenavigation.views.Component;
13
+
14
+import java.util.List;
15
+
16
+public class BottomTabOptionsPresenter {
17
+    private Options defaultOptions;
18
+    private final BottomTabFinder bottomTabFinder;
19
+    private BottomTabs bottomTabs;
20
+    private final int defaultSelectedTextColor;
21
+    private final int defaultTextColor;
22
+    private final List<ViewController> tabs;
23
+
24
+    public BottomTabOptionsPresenter(Context context, List<ViewController> tabs, Options defaultOptions) {
25
+        this.tabs = tabs;
26
+        this.bottomTabFinder = new BottomTabFinder(tabs);
27
+        this.defaultOptions = defaultOptions;
28
+        defaultSelectedTextColor = defaultOptions.bottomTabOptions.selectedIconColor.get(ContextCompat.getColor(context, com.aurelhubert.ahbottomnavigation.R.color.colorBottomNavigationAccent));
29
+        defaultTextColor = defaultOptions.bottomTabOptions.iconColor.get(ContextCompat.getColor(context, com.aurelhubert.ahbottomnavigation.R.color.colorBottomNavigationInactive));
30
+    }
31
+
32
+    public void setDefaultOptions(Options defaultOptions) {
33
+        this.defaultOptions = defaultOptions;
34
+    }
35
+
36
+    public void bindView(BottomTabs bottomTabs) {
37
+        this.bottomTabs = bottomTabs;
38
+    }
39
+
40
+    public void present() {
41
+        for (int i = 0; i < tabs.size(); i++) {
42
+            BottomTabOptions bottomTab = tabs.get(i).options.copy().withDefaultOptions(defaultOptions).bottomTabOptions;
43
+            bottomTabs.setBadge(i, bottomTab.badge.get(""));
44
+            bottomTabs.setTitleTypeface(i, bottomTab.fontFamily);
45
+            bottomTabs.setIconActiveColor(i, bottomTab.selectedIconColor.get(null));
46
+            bottomTabs.setIconInactiveColor(i, bottomTab.iconColor.get(null));
47
+            bottomTabs.setTitleActiveColor(i, bottomTab.selectedTextColor.get(null));
48
+            bottomTabs.setTitleInactiveColor(i, bottomTab.textColor.get(null));
49
+        }
50
+    }
51
+
52
+    public void mergeChildOptions(Options options, Component child) {
53
+        BottomTabOptions withDefaultOptions = options.withDefaultOptions(defaultOptions).bottomTabOptions;
54
+        int index = bottomTabFinder.findByComponent(child);
55
+        if (withDefaultOptions.badge.hasValue()) bottomTabs.setBadge(index, withDefaultOptions.badge.get());
56
+        if (withDefaultOptions.fontFamily != null) bottomTabs.setTitleTypeface(index, withDefaultOptions.fontFamily);
57
+        if (withDefaultOptions.selectedIconColor.hasValue()) bottomTabs.setIconActiveColor(index, withDefaultOptions.selectedIconColor.get());
58
+        if (withDefaultOptions.iconColor.hasValue()) bottomTabs.setIconInactiveColor(index, withDefaultOptions.iconColor.get());
59
+        if (withDefaultOptions.selectedTextColor.hasValue()) bottomTabs.setTitleActiveColor(index, withDefaultOptions.selectedTextColor.get());
60
+        if (withDefaultOptions.textColor.hasValue()) bottomTabs.setTitleInactiveColor(index, withDefaultOptions.textColor.get());
61
+    }
62
+
63
+    @VisibleForTesting
64
+    public int getDefaultSelectedTextColor() {
65
+        return defaultSelectedTextColor;
66
+    }
67
+
68
+    @VisibleForTesting
69
+    public int getDefaultTextColor() {
70
+        return defaultTextColor;
71
+    }
72
+}

+ 34
- 25
lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsOptionsPresenter.java Просмотреть файл

@@ -1,12 +1,13 @@
1 1
 package com.reactnativenavigation.presentation;
2 2
 
3
+import android.view.ViewGroup;
3 4
 import android.view.ViewGroup.MarginLayoutParams;
4 5
 
5 6
 import com.reactnativenavigation.anim.BottomTabsAnimator;
6 7
 import com.reactnativenavigation.parse.AnimationsOptions;
7
-import com.reactnativenavigation.parse.BottomTabOptions;
8 8
 import com.reactnativenavigation.parse.BottomTabsOptions;
9 9
 import com.reactnativenavigation.parse.Options;
10
+import com.reactnativenavigation.utils.UiUtils;
10 11
 import com.reactnativenavigation.viewcontrollers.ViewController;
11 12
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabFinder;
12 13
 import com.reactnativenavigation.viewcontrollers.bottomtabs.TabSelector;
@@ -16,44 +17,58 @@ import com.reactnativenavigation.views.Component;
16 17
 import java.util.List;
17 18
 
18 19
 public class BottomTabsOptionsPresenter {
19
-    private final BottomTabs bottomTabs;
20
-    private final TabSelector tabSelector;
21 20
     private final BottomTabFinder bottomTabFinder;
22
-    private final BottomTabsAnimator animator;
23 21
     private final List<ViewController> tabs;
22
+    private Options defaultOptions;
23
+    private BottomTabs bottomTabs;
24
+    private BottomTabsAnimator animator;
25
+    private TabSelector tabSelector;
24 26
 
25
-    public BottomTabsOptionsPresenter(BottomTabs bottomTabs, List<ViewController> tabs, TabSelector tabSelector, BottomTabFinder bottomTabFinder) {
26
-        this.bottomTabs = bottomTabs;
27
+    public BottomTabsOptionsPresenter(List<ViewController> tabs, Options defaultOptions) {
27 28
         this.tabs = tabs;
29
+        this.defaultOptions = defaultOptions;
30
+        this.bottomTabFinder = new BottomTabFinder(tabs);
31
+    }
32
+
33
+    public void setDefaultOptions(Options defaultOptions) {
34
+        this.defaultOptions = defaultOptions;
35
+    }
36
+
37
+    public void bindView(BottomTabs bottomTabs, TabSelector tabSelector) {
38
+        this.bottomTabs = bottomTabs;
28 39
         this.tabSelector = tabSelector;
29
-        this.bottomTabFinder = bottomTabFinder;
30 40
         animator = new BottomTabsAnimator(bottomTabs);
31 41
     }
32 42
 
43
+    public void applyLayoutParamsOptions(Options options, int tabIndex) {
44
+        Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
45
+        applyDrawBehind(withDefaultOptions.bottomTabsOptions, tabIndex);
46
+    }
47
+
33 48
     public void present(Options options) {
34
-        applyBottomTabsOptions(options.bottomTabsOptions, options.animations);
49
+        Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
50
+        applyBottomTabsOptions(withDefaultOptions.bottomTabsOptions, withDefaultOptions.animations);
35 51
     }
36 52
 
37 53
     public void presentChildOptions(Options options, Component child) {
38
-        applyBottomTabsOptions(options.bottomTabsOptions, options.animations);
54
+        Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
55
+        applyBottomTabsOptions(withDefaultOptions.bottomTabsOptions, withDefaultOptions.animations);
39 56
         int tabIndex = bottomTabFinder.findByComponent(child);
40
-        applyBottomTabOptions(options.bottomTabOptions, tabIndex);
41
-        applyDrawBehind(options.bottomTabsOptions, tabIndex);
42
-    }
43
-
44
-    private void applyBottomTabOptions(BottomTabOptions options, int tabIndex) {
45
-        if (options.badge.hasValue()) {
46
-            bottomTabs.setBadge(tabIndex, options.badge);
47
-        }
57
+        applyDrawBehind(withDefaultOptions.bottomTabsOptions, tabIndex);
48 58
     }
49 59
 
50 60
     private void applyDrawBehind(BottomTabsOptions options, int tabIndex) {
51
-        MarginLayoutParams lp = (MarginLayoutParams) tabs.get(tabIndex).getView().getLayoutParams();
61
+        ViewGroup tab = tabs.get(tabIndex).getView();
62
+        MarginLayoutParams lp = (MarginLayoutParams) tab.getLayoutParams();
52 63
         if (options.drawBehind.isTrue()) {
53 64
             lp.bottomMargin = 0;
54 65
         }
55 66
         if (options.visible.isTrueOrUndefined() && options.drawBehind.isFalseOrUndefined()) {
56
-            lp.bottomMargin = bottomTabs.getHeight();
67
+            if (bottomTabs.getHeight() == 0) {
68
+                UiUtils.runOnPreDrawOnce(bottomTabs, () -> lp.bottomMargin = bottomTabs.getHeight());
69
+            } else {
70
+                lp.bottomMargin = bottomTabs.getHeight();
71
+            }
57 72
         }
58 73
     }
59 74
 
@@ -71,12 +86,6 @@ public class BottomTabsOptionsPresenter {
71 86
         if (options.testId.hasValue()) {
72 87
             bottomTabs.setTag(options.testId.get());
73 88
         }
74
-        if (options.selectedTabColor.hasValue()) {
75
-            bottomTabs.setAccentColor(options.selectedTabColor.get());
76
-        }
77
-        if (options.tabColor.hasValue()) {
78
-            bottomTabs.setInactiveColor(options.tabColor.get());
79
-        }
80 89
         if (options.currentTabId.hasValue()) {
81 90
             int tabIndex = bottomTabFinder.findByControllerId(options.currentTabId.get());
82 91
             if (tabIndex >= 0) tabSelector.selectTab(tabIndex);

+ 23
- 9
lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java Просмотреть файл

@@ -4,6 +4,7 @@ import android.app.Activity;
4 4
 import android.graphics.Color;
5 5
 import android.os.Build;
6 6
 import android.view.View;
7
+import android.view.ViewGroup;
7 8
 import android.view.ViewGroup.MarginLayoutParams;
8 9
 
9 10
 import com.reactnativenavigation.parse.Options;
@@ -17,19 +18,36 @@ import com.reactnativenavigation.utils.UiUtils;
17 18
 public class OptionsPresenter {
18 19
 
19 20
     private Activity activity;
21
+    private Options defaultOptions;
20 22
 
21
-    public OptionsPresenter(Activity activity) {
23
+    public OptionsPresenter(Activity activity, Options defaultOptions) {
22 24
         this.activity = activity;
25
+        this.defaultOptions = defaultOptions;
26
+    }
27
+
28
+    public void setDefaultOptions(Options defaultOptions) {
29
+        this.defaultOptions = defaultOptions;
30
+    }
31
+
32
+    public void applyLayoutOptions(ViewGroup.LayoutParams layoutParams) {
33
+
23 34
     }
24 35
 
25 36
     public void present(View view, Options options) {
26
-        applyOrientation(options.layout.orientation);
27
-        applyViewOptions(view, options);
28
-        applyStatusBarOptions(view, options.statusBar);
37
+        Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
38
+        applyOrientation(withDefaultOptions.layout.orientation);
39
+        applyViewOptions(view, withDefaultOptions);
40
+        applyStatusBarOptions(view, withDefaultOptions.statusBar);
29 41
     }
30 42
 
31 43
     public void applyRootOptions(View view, Options options) {
32
-        setDrawBehindStatusBar(view, options.statusBar);
44
+        Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
45
+        setDrawBehindStatusBar(view, withDefaultOptions.statusBar);
46
+    }
47
+
48
+    public void onViewBroughtToFront(View view, Options options) {
49
+        Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
50
+        applyStatusBarOptions(view, withDefaultOptions.statusBar);
33 51
     }
34 52
 
35 53
     private void applyOrientation(OrientationOptions options) {
@@ -45,10 +63,6 @@ public class OptionsPresenter {
45 63
         }
46 64
     }
47 65
 
48
-    public void onViewBroughtToFront(View view, Options options) {
49
-        applyStatusBarOptions(view, options.statusBar);
50
-    }
51
-
52 66
     private void applyStatusBarOptions(View view, StatusBarOptions statusBar) {
53 67
         setStatusBarBackgroundColor(statusBar);
54 68
         setTextColorScheme(statusBar.textColorScheme);

+ 39
- 14
lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackOptionsPresenter.java Просмотреть файл

@@ -1,7 +1,9 @@
1 1
 package com.reactnativenavigation.presentation;
2 2
 
3 3
 import android.app.Activity;
4
+import android.content.Context;
4 5
 import android.graphics.Color;
6
+import android.view.View;
5 7
 import android.view.ViewGroup.LayoutParams;
6 8
 
7 9
 import com.reactnativenavigation.parse.AnimationsOptions;
@@ -24,23 +26,45 @@ public class StackOptionsPresenter {
24 26
     private final double defaultSubtitleFontSize;
25 27
 
26 28
     private TopBar topBar;
29
+    private Options defaultOptions;
27 30
 
28
-    public StackOptionsPresenter(TopBar topBar) {
31
+    public StackOptionsPresenter(Context context, Options defaultOptions) {
32
+        defaultTitleFontSize = UiUtils.dpToSp(context, 18);
33
+        defaultSubtitleFontSize = UiUtils.dpToSp(context, 14);
34
+        this.defaultOptions = defaultOptions;
35
+    }
36
+
37
+    public void setDefaultOptions(Options defaultOptions) {
38
+        this.defaultOptions = defaultOptions;
39
+    }
40
+
41
+    public void bindView(TopBar topBar) {
29 42
         this.topBar = topBar;
30
-        defaultTitleFontSize = UiUtils.dpToSp(topBar.getContext(), 18);
31
-        defaultSubtitleFontSize = UiUtils.dpToSp(topBar.getContext(), 14);
43
+    }
44
+
45
+    public void applyLayoutParamsOptions(Options options, View view) {
46
+        Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
47
+        if (view instanceof Component) {
48
+            if (withDefaultOptions.topBar.drawBehind.isTrue() && !withDefaultOptions.layout.topMargin.hasValue()) {
49
+                ((Component) view).drawBehindTopBar();
50
+            } else if (options.topBar.drawBehind.isFalseOrUndefined()) {
51
+                ((Component) view).drawBelowTopBar(topBar);
52
+            }
53
+        }
32 54
     }
33 55
 
34 56
     public void applyChildOptions(Options options, Component child) {
35
-        applyOrientation(options.layout.orientation);
36
-        applyButtons(options.topBar.buttons);
37
-        applyTopBarOptions(options.topBar, options.animations, child, options);
38
-        applyTopTabsOptions(options.topTabs);
39
-        applyTopTabOptions(options.topTabOptions);
57
+        Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
58
+        applyOrientation(withDefaultOptions.layout.orientation);
59
+        applyButtons(withDefaultOptions.topBar.buttons);
60
+        applyTopBarOptions(withDefaultOptions.topBar, withDefaultOptions.animations, child, options);
61
+        applyTopTabsOptions(withDefaultOptions.topTabs);
62
+        applyTopTabOptions(withDefaultOptions.topTabOptions);
40 63
     }
41 64
 
42 65
     public void applyOrientation(OrientationOptions options) {
43
-        ((Activity) topBar.getContext()).setRequestedOrientation(options.getValue());
66
+        OrientationOptions withDefaultOptions = options.copy().mergeWithDefault(defaultOptions.layout.orientation);
67
+        ((Activity) topBar.getContext()).setRequestedOrientation(withDefaultOptions.getValue());
44 68
     }
45 69
 
46 70
     private void applyTopBarOptions(TopBarOptions options, AnimationsOptions animationOptions, Component component, Options componentOptions) {
@@ -124,11 +148,12 @@ public class StackOptionsPresenter {
124 148
     }
125 149
 
126 150
     public void mergeChildOptions(Options options, Component child) {
127
-        mergeOrientation(options.layout.orientation);
128
-        mergeButtons(options.topBar.buttons);
129
-        mergeTopBarOptions(options.topBar, options.animations, child);
130
-        mergeTopTabsOptions(options.topTabs);
131
-        mergeTopTabOptions(options.topTabOptions);
151
+        Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
152
+        mergeOrientation(withDefaultOptions.layout.orientation);
153
+        mergeButtons(withDefaultOptions.topBar.buttons);
154
+        mergeTopBarOptions(withDefaultOptions.topBar, withDefaultOptions.animations, child);
155
+        mergeTopTabsOptions(withDefaultOptions.topTabs);
156
+        mergeTopTabOptions(withDefaultOptions.topTabOptions);
132 157
     }
133 158
 
134 159
     private void mergeOrientation(OrientationOptions orientationOptions) {

+ 1
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/ViewUtils.java Просмотреть файл

@@ -76,6 +76,7 @@ public class ViewUtils {
76 76
     }
77 77
 
78 78
     public static int getPreferredHeight(View view) {
79
+        if (view.getLayoutParams() == null) return 0;
79 80
         return view.getLayoutParams().height < 0 ? view.getHeight() : view.getLayoutParams().height;
80 81
     }
81 82
 }

+ 5
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ChildController.java Просмотреть файл

@@ -20,6 +20,11 @@ public abstract class ChildController<T extends ViewGroup> extends ViewControlle
20 20
         this.childRegistry = childRegistry;
21 21
     }
22 22
 
23
+    @Override
24
+    public void setDefaultOptions(Options defaultOptions) {
25
+        presenter.setDefaultOptions(defaultOptions);
26
+    }
27
+
23 28
     @Override
24 29
     public void onViewAppeared() {
25 30
         super.onViewAppeared();

+ 3
- 2
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java Просмотреть файл

@@ -19,8 +19,9 @@ public class ComponentViewController extends ChildController<ComponentLayout> {
19 19
                                    final String id,
20 20
                                    final String componentName,
21 21
                                    final ReactViewCreator viewCreator,
22
-                                   final Options initialOptions) {
23
-        super(activity, childRegistry, id, new OptionsPresenter(activity), initialOptions);
22
+                                   final Options initialOptions,
23
+                                   final OptionsPresenter presenter) {
24
+        super(activity, childRegistry, id, presenter, initialOptions);
24 25
         this.componentName = componentName;
25 26
         this.viewCreator = viewCreator;
26 27
     }

+ 8
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java Просмотреть файл

@@ -33,8 +33,10 @@ public class Navigator extends ParentController implements JsDevReloadHandler.Re
33 33
     private final OverlayManager overlayManager;
34 34
     private Options defaultOptions = new Options();
35 35
 
36
+    @Override
36 37
     public void setDefaultOptions(Options defaultOptions) {
37 38
         this.defaultOptions = defaultOptions;
39
+        if (root != null) root.setDefaultOptions(defaultOptions);
38 40
     }
39 41
 
40 42
     public Options getDefaultOptions() {
@@ -42,7 +44,7 @@ public class Navigator extends ParentController implements JsDevReloadHandler.Re
42 44
     }
43 45
 
44 46
     public Navigator(final Activity activity, ChildControllersRegistry childRegistry, OverlayManager overlayManager) {
45
-        super(activity, childRegistry,"navigator" + CompatUtils.generateViewId(), new OptionsPresenter(activity), new Options());
47
+        super(activity, childRegistry,"navigator" + CompatUtils.generateViewId(), new OptionsPresenter(activity, new Options()), new Options());
46 48
         modalStack = new ModalStack(new ModalPresenter(new ModalAnimator(activity)));
47 49
         this.overlayManager = overlayManager;
48 50
     }
@@ -73,6 +75,11 @@ public class Navigator extends ParentController implements JsDevReloadHandler.Re
73 75
         return modalStack.handleBack(listener, root);
74 76
     }
75 77
 
78
+    @Override
79
+    protected ViewController getCurrentChild() {
80
+        return root;
81
+    }
82
+
76 83
     @Override
77 84
     public void onReload() {
78 85
         destroyViews();

+ 23
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java Просмотреть файл

@@ -2,6 +2,7 @@ package com.reactnativenavigation.viewcontrollers;
2 2
 
3 3
 import android.app.Activity;
4 4
 import android.support.annotation.CallSuper;
5
+import android.support.annotation.CheckResult;
5 6
 import android.support.annotation.NonNull;
6 7
 import android.support.annotation.Nullable;
7 8
 import android.support.v4.view.ViewPager;
@@ -9,6 +10,7 @@ import android.view.ViewGroup;
9 10
 
10 11
 import com.reactnativenavigation.parse.Options;
11 12
 import com.reactnativenavigation.presentation.OptionsPresenter;
13
+import com.reactnativenavigation.utils.CollectionUtils;
12 14
 import com.reactnativenavigation.views.Component;
13 15
 
14 16
 import java.util.Collection;
@@ -19,7 +21,27 @@ public abstract class ParentController<T extends ViewGroup> extends ChildControl
19 21
 		super(activity, childRegistry, id, presenter, initialOptions);
20 22
 	}
21 23
 
22
-	@NonNull
24
+	@Override
25
+    public void setDefaultOptions(Options defaultOptions) {
26
+        Collection<? extends ViewController> children = getChildControllers();
27
+        if (!CollectionUtils.isNullOrEmpty(children)) {
28
+            for (ViewController child : children) {
29
+                child.setDefaultOptions(defaultOptions);
30
+            }
31
+        }
32
+    }
33
+
34
+    @Override
35
+    @CheckResult
36
+    public Options resolveCurrentOptions() {
37
+	    if (CollectionUtils.isNullOrEmpty(getChildControllers())) return options;
38
+        Options o = getCurrentChild().resolveCurrentOptions();
39
+        return o.copy().mergeWith(options);
40
+    }
41
+
42
+    protected abstract ViewController getCurrentChild();
43
+
44
+    @NonNull
23 45
 	@Override
24 46
 	public T getView() {
25 47
 		return (T) super.getView();

+ 13
- 3
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/SideMenuController.java Просмотреть файл

@@ -23,11 +23,21 @@ public class SideMenuController extends ParentController<DrawerLayout> {
23 23
 	private ViewController leftController;
24 24
 	private ViewController rightController;
25 25
 
26
-	public SideMenuController(Activity activity, ChildControllersRegistry childRegistry, String id, Options initialOptions) {
27
-		super(activity, childRegistry, id, new OptionsPresenter(activity), initialOptions);
26
+	public SideMenuController(Activity activity, ChildControllersRegistry childRegistry, String id, Options initialOptions, OptionsPresenter presenter) {
27
+		super(activity, childRegistry, id, presenter, initialOptions);
28 28
 	}
29 29
 
30
-	@NonNull
30
+    @Override
31
+    protected ViewController getCurrentChild() {
32
+	    if (getView().isDrawerOpen(Gravity.LEFT)) {
33
+            return leftController;
34
+        } else if (getView().isDrawerOpen(Gravity.RIGHT)) {
35
+            return rightController;
36
+        }
37
+        return centerController;
38
+    }
39
+
40
+    @NonNull
31 41
 	@Override
32 42
 	protected DrawerLayout createView() {
33 43
         return new DrawerLayout(getActivity());

+ 10
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java Просмотреть файл

@@ -2,6 +2,7 @@ package com.reactnativenavigation.viewcontrollers;
2 2
 
3 3
 import android.app.Activity;
4 4
 import android.support.annotation.CallSuper;
5
+import android.support.annotation.CheckResult;
5 6
 import android.support.annotation.NonNull;
6 7
 import android.support.annotation.Nullable;
7 8
 import android.support.annotation.VisibleForTesting;
@@ -74,6 +75,11 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
74 75
         return false;
75 76
     }
76 77
 
78
+    @CheckResult
79
+    public Options resolveCurrentOptions() {
80
+        return options;
81
+    }
82
+
77 83
     @CallSuper
78 84
     public void mergeOptions(Options options) {
79 85
         this.options = this.options.mergeWith(options);
@@ -86,6 +92,10 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
86 92
 
87 93
     }
88 94
 
95
+    public void setDefaultOptions(Options defaultOptions) {
96
+        
97
+    }
98
+
89 99
     public Activity getActivity() {
90 100
         return activity;
91 101
     }

+ 4
- 4
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabFinder.java Просмотреть файл

@@ -10,6 +10,10 @@ import java.util.List;
10 10
 public class BottomTabFinder {
11 11
     private List<ViewController> tabs;
12 12
 
13
+    public BottomTabFinder(List<ViewController> tabs) {
14
+        this.tabs = tabs;
15
+    }
16
+
13 17
     @IntRange(from = -1)
14 18
     public int findByComponent(Component component) {
15 19
         for (int i = 0; i < tabs.size(); i++) {
@@ -29,8 +33,4 @@ public class BottomTabFinder {
29 33
         }
30 34
         return -1;
31 35
     }
32
-
33
-    void setTabs(List<ViewController> tabs) {
34
-        this.tabs = tabs;
35
-    }
36 36
 }

+ 31
- 18
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java Просмотреть файл

@@ -12,6 +12,7 @@ import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
12 12
 import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
13 13
 import com.reactnativenavigation.parse.BottomTabOptions;
14 14
 import com.reactnativenavigation.parse.Options;
15
+import com.reactnativenavigation.presentation.BottomTabOptionsPresenter;
15 16
 import com.reactnativenavigation.presentation.BottomTabsOptionsPresenter;
16 17
 import com.reactnativenavigation.presentation.OptionsPresenter;
17 18
 import com.reactnativenavigation.react.EventEmitter;
@@ -39,26 +40,36 @@ public class BottomTabsController extends ParentController implements AHBottomNa
39 40
     private EventEmitter eventEmitter;
40 41
     private ImageLoader imageLoader;
41 42
     private BottomTabsOptionsPresenter presenter;
42
-    private final BottomTabFinder bottomTabFinder = new BottomTabFinder();
43
+    private BottomTabOptionsPresenter tabPresenter;
43 44
 
44
-    public BottomTabsController(Activity activity, List<ViewController> tabs, ChildControllersRegistry childRegistry, EventEmitter eventEmitter, ImageLoader imageLoader, String id, Options initialOptions) {
45
-		super(activity, childRegistry, id, new OptionsPresenter(activity), initialOptions);
45
+    public BottomTabsController(Activity activity, List<ViewController> tabs, ChildControllersRegistry childRegistry, EventEmitter eventEmitter, ImageLoader imageLoader, String id, Options initialOptions, OptionsPresenter presenter, BottomTabsOptionsPresenter bottomTabsPresenter, BottomTabOptionsPresenter bottomTabPresenter) {
46
+		super(activity, childRegistry, id, presenter, initialOptions);
46 47
         this.tabs = tabs;
47 48
         this.eventEmitter = eventEmitter;
48 49
         this.imageLoader = imageLoader;
49
-        createTabs(tabs);
50
+        this.presenter = bottomTabsPresenter;
51
+        this.tabPresenter = bottomTabPresenter;
50 52
     }
51 53
 
52
-	@NonNull
54
+    @Override
55
+    public void setDefaultOptions(Options defaultOptions) {
56
+        super.setDefaultOptions(defaultOptions);
57
+        presenter.setDefaultOptions(defaultOptions);
58
+        tabPresenter.setDefaultOptions(defaultOptions);
59
+    }
60
+
61
+    @NonNull
53 62
 	@Override
54 63
 	protected ViewGroup createView() {
55 64
 		RelativeLayout root = new RelativeLayout(getActivity());
56 65
 		bottomTabs = new BottomTabs(getActivity());
57
-        presenter = new BottomTabsOptionsPresenter(bottomTabs, tabs, this, bottomTabFinder);
66
+        presenter.bindView(bottomTabs, this);
67
+        tabPresenter.bindView(bottomTabs);
58 68
         bottomTabs.setOnTabSelectedListener(this);
59 69
 		RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
60 70
 		lp.addRule(ALIGN_PARENT_BOTTOM);
61 71
 		root.addView(bottomTabs, lp);
72
+		createTabs(root);
62 73
 		return root;
63 74
 	}
64 75
 
@@ -66,6 +77,7 @@ public class BottomTabsController extends ParentController implements AHBottomNa
66 77
     public void applyOptions(Options options) {
67 78
         super.applyOptions(options);
68 79
         presenter.present(options);
80
+        tabPresenter.present();
69 81
     }
70 82
 
71 83
     @Override
@@ -81,6 +93,7 @@ public class BottomTabsController extends ParentController implements AHBottomNa
81 93
     public void mergeChildOptions(Options options, Component child) {
82 94
         super.mergeChildOptions(options, child);
83 95
         presenter.presentChildOptions(options, child);
96
+        tabPresenter.mergeChildOptions(options, child);
84 97
         applyOnParentController(parentController ->
85 98
                 ((ParentController) parentController).mergeChildOptions(options.copy().clearBottomTabsOptions(), child)
86 99
         );
@@ -93,10 +106,11 @@ public class BottomTabsController extends ParentController implements AHBottomNa
93 106
 
94 107
     @Override
95 108
     public void sendOnNavigationButtonPressed(String buttonId) {
96
-        getCurrentTab().sendOnNavigationButtonPressed(buttonId);
109
+        getCurrentChild().sendOnNavigationButtonPressed(buttonId);
97 110
     }
98 111
 
99
-    private ViewController getCurrentTab() {
112
+    @Override
113
+    protected ViewController getCurrentChild() {
100 114
         return tabs.get(bottomTabs.getCurrentItem());
101 115
     }
102 116
 
@@ -105,16 +119,13 @@ public class BottomTabsController extends ParentController implements AHBottomNa
105 119
         if (wasSelected) return false;
106 120
         eventEmitter.emitBottomTabSelected(bottomTabs.getCurrentItem(), index);
107 121
         selectTab(index);
108
-        return true;
122
+        return false;
109 123
 	}
110 124
 
111
-	private void createTabs(final List<ViewController> tabs) {
125
+	private void createTabs(RelativeLayout root) {
112 126
 		if (tabs.size() > 5) {
113 127
 			throw new RuntimeException("Too many tabs!");
114 128
 		}
115
-		this.tabs = tabs;
116
-        bottomTabFinder.setTabs(tabs);
117
-        getView();
118 129
         List<String> icons = new ArrayList<>();
119 130
         List<BottomTabOptions> bottomTabOptionsList = new ArrayList<>();
120 131
         for (int i = 0; i < tabs.size(); i++) {
@@ -133,7 +144,7 @@ public class BottomTabsController extends ParentController implements AHBottomNa
133 144
             public void onComplete(@NonNull List<Drawable> drawables) {
134 145
                 List<AHBottomNavigationItem> tabs = new ArrayList<>();
135 146
                 for (int i = 0; i < drawables.size(); i++) {
136
-                    tabs.add(new AHBottomNavigationItem(bottomTabOptionsList.get(i).title.get(""), drawables.get(i)));
147
+                    tabs.add(new AHBottomNavigationItem(bottomTabOptionsList.get(i).text.get(""), drawables.get(i)));
137 148
                 }
138 149
                 bottomTabs.addItems(tabs);
139 150
                 bottomTabs.post(() -> {
@@ -141,7 +152,7 @@ public class BottomTabsController extends ParentController implements AHBottomNa
141 152
                         bottomTabs.setTabTestId(i, bottomTabOptionsList.get(i).testId);
142 153
                     }
143 154
                 });
144
-                attachTabs();
155
+                attachTabs(root);
145 156
             }
146 157
 
147 158
             @Override
@@ -149,14 +160,16 @@ public class BottomTabsController extends ParentController implements AHBottomNa
149 160
                 error.printStackTrace();
150 161
             }
151 162
         });
152
-
153 163
 	}
154 164
 
155
-    private void attachTabs() {
165
+    private void attachTabs(RelativeLayout root) {
156 166
         for (int i = (tabs.size() - 1); i >= 0; i--) {
157 167
             ViewGroup tab = tabs.get(i).getView();
168
+            tab.setLayoutParams(new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
169
+            Options options = resolveCurrentOptions();
170
+            presenter.applyLayoutParamsOptions(options, i);
158 171
             if (i != 0) tab.setVisibility(View.INVISIBLE);
159
-            getView().addView(tab);
172
+            root.addView(tab);
160 173
         }
161 174
     }
162 175
 

+ 8
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/BackButtonHelper.java Просмотреть файл

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

+ 52
- 14
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java Просмотреть файл

@@ -5,10 +5,13 @@ import android.support.annotation.NonNull;
5 5
 import android.support.annotation.RestrictTo;
6 6
 import android.support.annotation.VisibleForTesting;
7 7
 import android.support.v4.view.ViewPager;
8
+import android.view.ViewGroup;
9
+import android.widget.RelativeLayout;
8 10
 
9 11
 import com.reactnativenavigation.anim.NavigationAnimator;
10 12
 import com.reactnativenavigation.parse.Options;
11 13
 import com.reactnativenavigation.presentation.OptionsPresenter;
14
+import com.reactnativenavigation.presentation.StackOptionsPresenter;
12 15
 import com.reactnativenavigation.react.Constants;
13 16
 import com.reactnativenavigation.utils.CommandListener;
14 17
 import com.reactnativenavigation.utils.CommandListenerAdapter;
@@ -27,6 +30,7 @@ import com.reactnativenavigation.views.topbar.TopBar;
27 30
 
28 31
 import java.util.Collection;
29 32
 import java.util.Iterator;
33
+import java.util.List;
30 34
 
31 35
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
32 36
 
@@ -39,21 +43,38 @@ public class StackController extends ParentController<StackLayout> {
39 43
     private TopBarBackgroundViewController topBarBackgroundViewController;
40 44
     private TopBarController topBarController;
41 45
     private BackButtonHelper backButtonHelper;
46
+    private final StackOptionsPresenter presenter;
42 47
 
43
-    public StackController(Activity activity, ChildControllersRegistry childRegistry, ReactViewCreator topBarButtonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, NavigationAnimator animator, String id, Options initialOptions, BackButtonHelper backButtonHelper) {
44
-        super(activity, childRegistry, id, new OptionsPresenter(activity), initialOptions);
48
+    public StackController(Activity activity, List<ViewController> children, ChildControllersRegistry childRegistry, ReactViewCreator topBarButtonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, NavigationAnimator animator, String id, Options initialOptions, BackButtonHelper backButtonHelper, StackOptionsPresenter stackPresenter, OptionsPresenter presenter) {
49
+        super(activity, childRegistry, id, presenter, initialOptions);
45 50
         this.topBarController = topBarController;
46 51
         this.topBarButtonCreator = topBarButtonCreator;
47 52
         this.titleBarReactViewCreator = titleBarReactViewCreator;
48 53
         this.topBarBackgroundViewController = topBarBackgroundViewController;
49 54
         this.animator = animator;
50 55
         this.backButtonHelper = backButtonHelper;
56
+        this.presenter = stackPresenter;
57
+        for (ViewController child : children) {
58
+            stack.push(child.getId(), child);
59
+            child.setParentController(this);
60
+        }
61
+    }
62
+
63
+    @Override
64
+    public void setDefaultOptions(Options defaultOptions) {
65
+        super.setDefaultOptions(defaultOptions);
66
+        presenter.setDefaultOptions(defaultOptions);
67
+    }
68
+
69
+    @Override
70
+    protected ViewController getCurrentChild() {
71
+        return stack.peek();
51 72
     }
52 73
 
53 74
     @Override
54 75
     public void applyChildOptions(Options options, Component child) {
55 76
         super.applyChildOptions(options, child);
56
-        getView().applyChildOptions(this.options, child);
77
+        presenter.applyChildOptions(options, child);
57 78
         if (child instanceof ReactComponent) {
58 79
             fabOptionsPresenter.applyOptions(this.options.fabOptions, (ReactComponent) child, getView());
59 80
         }
@@ -74,7 +95,7 @@ public class StackController extends ParentController<StackLayout> {
74 95
     @Override
75 96
     public void mergeChildOptions(Options options, Component child) {
76 97
         super.mergeChildOptions(options, child);
77
-        getView().mergeChildOptions(options, child);
98
+        presenter.mergeChildOptions(options, child);
78 99
         animator.mergeOptions(options.animations);
79 100
         if (options.fabOptions.hasValue() && child instanceof ReactComponent) {
80 101
             fabOptionsPresenter.mergeOptions(options.fabOptions, (ReactComponent) child, getView());
@@ -108,8 +129,11 @@ public class StackController extends ParentController<StackLayout> {
108 129
         final ViewController toRemove = stack.peek();
109 130
         child.setParentController(this);
110 131
         stack.push(child.getId(), child);
111
-        addBackButton(child);
112
-        getView().addView(child.getView(), MATCH_PARENT, MATCH_PARENT);
132
+        backButtonHelper.addToPushedChild(this, child);
133
+        ViewGroup childView = child.getView();
134
+        childView.setLayoutParams(new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
135
+        presenter.applyLayoutParamsOptions(resolveCurrentOptions(), childView);
136
+        getView().addView(childView);
113 137
 
114 138
         if (toRemove != null) {
115 139
             if (child.options.animations.push.enable.isTrueOrUndefined()) {
@@ -126,11 +150,8 @@ public class StackController extends ParentController<StackLayout> {
126 150
         }
127 151
     }
128 152
 
129
-    private void addBackButton(ViewController child) {
130
-        backButtonHelper.addToChild(this, child);
131
-    }
132
-
133 153
     public void setRoot(ViewController child, CommandListener listener) {
154
+        backButtonHelper.clear(child);
134 155
         push(child, new CommandListenerAdapter() {
135 156
             @Override
136 157
             public void onSuccess(String childId) {
@@ -160,9 +181,14 @@ public class StackController extends ParentController<StackLayout> {
160 181
         final ViewController appearing = stack.peek();
161 182
         disappearing.onViewWillDisappear();
162 183
         appearing.onViewWillAppear();
163
-        getView().addView(appearing.getView(), 0);
164
-        getView().onChildWillAppear(appearing, disappearing);
165
-
184
+        ViewGroup appearingView = appearing.getView();
185
+        if (appearingView.getLayoutParams() == null) {
186
+            appearingView.setLayoutParams(new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
187
+            Options options = resolveCurrentOptions();
188
+            presenter.applyLayoutParamsOptions(options, appearingView);
189
+        }
190
+        getView().addView(appearingView, 0);
191
+        presenter.onChildWillAppear(appearing.options, disappearing.options);
166 192
         if (disappearing.options.animations.pop.enable.isTrueOrUndefined()) {
167 193
             animator.pop(disappearing.getView(), () -> finishPopping(disappearing, listener));
168 194
         } else {
@@ -256,7 +282,7 @@ public class StackController extends ParentController<StackLayout> {
256 282
     @NonNull
257 283
     @Override
258 284
     protected StackLayout createView() {
259
-        return new StackLayout(getActivity(),
285
+        StackLayout stackLayout = new StackLayout(getActivity(),
260 286
                 topBarButtonCreator,
261 287
                 titleBarReactViewCreator,
262 288
                 topBarBackgroundViewController,
@@ -264,6 +290,18 @@ public class StackController extends ParentController<StackLayout> {
264 290
                 this::onNavigationButtonPressed,
265 291
                 getId()
266 292
         );
293
+        presenter.bindView(topBarController.getView());
294
+        addInitialChild(stackLayout);
295
+        return stackLayout;
296
+    }
297
+
298
+    private void addInitialChild(StackLayout stackLayout) {
299
+        if (isEmpty()) return;
300
+        ViewGroup child = peek().getView();
301
+        child.setLayoutParams(new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
302
+        Options options = resolveCurrentOptions();
303
+        presenter.applyLayoutParamsOptions(options, child);
304
+        stackLayout.addView(child);
267 305
     }
268 306
 
269 307
     private void onNavigationButtonPressed(String buttonId) {

+ 39
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java Просмотреть файл

@@ -4,12 +4,18 @@ import android.app.Activity;
4 4
 
5 5
 import com.reactnativenavigation.anim.NavigationAnimator;
6 6
 import com.reactnativenavigation.parse.Options;
7
+import com.reactnativenavigation.presentation.OptionsPresenter;
8
+import com.reactnativenavigation.presentation.StackOptionsPresenter;
7 9
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
8 10
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
11
+import com.reactnativenavigation.viewcontrollers.ViewController;
9 12
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
10 13
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
11 14
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
12 15
 
16
+import java.util.ArrayList;
17
+import java.util.List;
18
+
13 19
 public class StackControllerBuilder {
14 20
     private Activity activity;
15 21
     private ChildControllersRegistry childRegistry;
@@ -21,12 +27,31 @@ public class StackControllerBuilder {
21 27
     private Options initialOptions = new Options();
22 28
     private NavigationAnimator animator;
23 29
     private BackButtonHelper backButtonHelper = new BackButtonHelper();
30
+    private OptionsPresenter presenter;
31
+    private StackOptionsPresenter stackPresenter;
32
+    private List<ViewController> children = new ArrayList<>();
24 33
 
25 34
     public StackControllerBuilder(Activity activity) {
26 35
         this.activity = activity;
36
+        presenter = new OptionsPresenter(activity, new Options());
27 37
         animator = new NavigationAnimator(activity);
28 38
     }
29 39
 
40
+    public StackControllerBuilder setChildren(List<ViewController> children) {
41
+        this.children = children;
42
+        return this;
43
+    }
44
+
45
+    public StackControllerBuilder setOptionsPresenter(OptionsPresenter presenter) {
46
+        this.presenter = presenter;
47
+        return this;
48
+    }
49
+
50
+    public StackControllerBuilder setStackPresenter(StackOptionsPresenter stackPresenter) {
51
+        this.stackPresenter = stackPresenter;
52
+        return this;
53
+    }
54
+
30 55
     public StackControllerBuilder setChildRegistry(ChildControllersRegistry childRegistry) {
31 56
         this.childRegistry = childRegistry;
32 57
         return this;
@@ -73,6 +98,19 @@ public class StackControllerBuilder {
73 98
     }
74 99
 
75 100
     public StackController build() {
76
-        return new StackController(activity, childRegistry, topBarButtonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarController, animator, id, initialOptions, backButtonHelper);
101
+        return new StackController(activity,
102
+                children,
103
+                childRegistry,
104
+                topBarButtonCreator,
105
+                titleBarReactViewCreator,
106
+                topBarBackgroundViewController,
107
+                topBarController,
108
+                animator,
109
+                id,
110
+                initialOptions,
111
+                backButtonHelper,
112
+                stackPresenter,
113
+                presenter
114
+        );
77 115
     }
78 116
 }

+ 7
- 2
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java Просмотреть файл

@@ -24,8 +24,8 @@ public class TopTabsController extends ParentController<TopTabsViewPager> {
24 24
     private List<ViewController> tabs;
25 25
     private TopTabsLayoutCreator viewCreator;
26 26
 
27
-    public TopTabsController(Activity activity, ChildControllersRegistry childRegistry, String id, List<ViewController> tabs, TopTabsLayoutCreator viewCreator, Options options) {
28
-        super(activity, childRegistry, id, new OptionsPresenter(activity), options);
27
+    public TopTabsController(Activity activity, ChildControllersRegistry childRegistry, String id, List<ViewController> tabs, TopTabsLayoutCreator viewCreator, Options options, OptionsPresenter presenter) {
28
+        super(activity, childRegistry, id, presenter, options);
29 29
         this.viewCreator = viewCreator;
30 30
         this.tabs = tabs;
31 31
         for (ViewController tab : tabs) {
@@ -39,6 +39,11 @@ public class TopTabsController extends ParentController<TopTabsViewPager> {
39 39
         }
40 40
     }
41 41
 
42
+    @Override
43
+    protected ViewController getCurrentChild() {
44
+        return tabs.get(getView().getCurrentItem());
45
+    }
46
+
42 47
     @NonNull
43 48
     @Override
44 49
     protected TopTabsViewPager createView() {

+ 2
- 12
lib/android/app/src/main/java/com/reactnativenavigation/views/BottomTabs.java Просмотреть файл

@@ -25,8 +25,8 @@ public class BottomTabs extends AHBottomNavigation {
25 25
         if (BuildConfig.DEBUG) view.setContentDescription(testId.get());
26 26
     }
27 27
 
28
-    public void setBadge(int bottomTabIndex, Text badge) {
29
-        setNotification(badge.get(), bottomTabIndex);
28
+    public void setBadge(int bottomTabIndex, String badge) {
29
+        setNotification(badge, bottomTabIndex);
30 30
     }
31 31
 
32 32
     @Override
@@ -34,16 +34,6 @@ public class BottomTabs extends AHBottomNavigation {
34 34
         super.setCurrentItem(position);
35 35
     }
36 36
 
37
-    @Override
38
-    public void setAccentColor(int accentColor) {
39
-        if (getAccentColor() != accentColor) super.setAccentColor(accentColor);
40
-    }
41
-
42
-    @Override
43
-    public void setInactiveColor(int inactiveColor) {
44
-        if (getInactiveColor() != inactiveColor) super.setInactiveColor(inactiveColor);
45
-    }
46
-
47 37
     @Override
48 38
     public void setTitleState(TitleState titleState) {
49 39
         if (getTitleState() != titleState) super.setTitleState(titleState);

+ 9
- 3
lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentLayout.java Просмотреть файл

@@ -9,6 +9,7 @@ import android.widget.RelativeLayout;
9 9
 
10 10
 import com.reactnativenavigation.interfaces.ScrollEventListener;
11 11
 import com.reactnativenavigation.parse.Options;
12
+import com.reactnativenavigation.utils.UiUtils;
12 13
 import com.reactnativenavigation.utils.ViewUtils;
13 14
 import com.reactnativenavigation.viewcontrollers.IReactView;
14 15
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
@@ -77,7 +78,7 @@ public class ComponentLayout extends FrameLayout implements ReactComponent, TopB
77 78
 
78 79
     @Override
79 80
     public void drawBehindTopBar() {
80
-        if (getParent() instanceof RelativeLayout) {
81
+        if (getLayoutParams() instanceof RelativeLayout.LayoutParams) {
81 82
             RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
82 83
             layoutParams.topMargin = 0;
83 84
             setLayoutParams(layoutParams);
@@ -86,9 +87,14 @@ public class ComponentLayout extends FrameLayout implements ReactComponent, TopB
86 87
 
87 88
     @Override
88 89
     public void drawBelowTopBar(TopBar topBar) {
89
-        if (getParent() instanceof RelativeLayout) {
90
+        if (getLayoutParams() instanceof RelativeLayout.LayoutParams) {
90 91
             RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
91
-            layoutParams.topMargin = ViewUtils.getPreferredHeight(topBar);
92
+            int topBarHeight = ViewUtils.getPreferredHeight(topBar);
93
+            if (topBarHeight == 0) {
94
+                UiUtils.runOnPreDrawOnce(topBar, () -> layoutParams.topMargin = topBar.getHeight());
95
+            } else {
96
+                layoutParams.topMargin = topBarHeight;
97
+            }
92 98
             setLayoutParams(layoutParams);
93 99
         }
94 100
     }

+ 0
- 21
lib/android/app/src/main/java/com/reactnativenavigation/views/StackLayout.java Просмотреть файл

@@ -4,11 +4,8 @@ import android.annotation.SuppressLint;
4 4
 import android.content.Context;
5 5
 import android.widget.RelativeLayout;
6 6
 
7
-import com.reactnativenavigation.parse.Options;
8
-import com.reactnativenavigation.presentation.StackOptionsPresenter;
9 7
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
10 8
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
11
-import com.reactnativenavigation.viewcontrollers.ViewController;
12 9
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
13 10
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
14 11
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
@@ -19,13 +16,11 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
19 16
 @SuppressLint("ViewConstructor")
20 17
 public class StackLayout extends RelativeLayout {
21 18
     private String stackId;
22
-    private final StackOptionsPresenter optionsPresenter;
23 19
 
24 20
     public StackLayout(Context context, ReactViewCreator topBarButtonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, TopBarButtonController.OnClickListener topBarButtonClickListener, String stackId) {
25 21
         super(context);
26 22
         this.stackId = stackId;
27 23
         createLayout(topBarButtonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarController, topBarButtonClickListener);
28
-        optionsPresenter = new StackOptionsPresenter(topBarController.getView());
29 24
         setContentDescription("StackLayout");
30 25
     }
31 26
 
@@ -33,22 +28,6 @@ public class StackLayout extends RelativeLayout {
33 28
         addView(topBarController.createView(getContext(), buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, this), MATCH_PARENT, WRAP_CONTENT);
34 29
     }
35 30
 
36
-    public void applyChildOptions(Options options) {
37
-        optionsPresenter.applyOrientation(options.layout.orientation);
38
-    }
39
-
40
-    public void applyChildOptions(Options options, Component child) {
41
-        optionsPresenter.applyChildOptions(options, child);
42
-    }
43
-
44
-    public void onChildWillAppear(ViewController appearing, ViewController disappearing) {
45
-        optionsPresenter.onChildWillAppear(appearing.options, disappearing.options);
46
-    }
47
-
48
-    public void mergeChildOptions(Options options, Component child) {
49
-        optionsPresenter.mergeChildOptions(options, child);
50
-    }
51
-
52 31
     public String getStackId() {
53 32
         return stackId;
54 33
     }

+ 17
- 1
lib/android/app/src/test/java/com/reactnativenavigation/TestUtils.java Просмотреть файл

@@ -1,15 +1,23 @@
1 1
 package com.reactnativenavigation;
2 2
 
3 3
 import android.app.Activity;
4
+import android.content.Context;
4 5
 
5 6
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
6 7
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
7 8
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
8 9
 import com.reactnativenavigation.parse.Options;
10
+import com.reactnativenavigation.presentation.StackOptionsPresenter;
11
+import com.reactnativenavigation.utils.ImageLoader;
9 12
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
13
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
14
+import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
10 15
 import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
11 16
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
12 17
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
18
+import com.reactnativenavigation.views.StackLayout;
19
+import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
20
+import com.reactnativenavigation.views.topbar.TopBar;
13 21
 
14 22
 public class TestUtils {
15 23
     public static StackControllerBuilder newStackController(Activity activity) {
@@ -19,7 +27,15 @@ public class TestUtils {
19 27
                 .setTopBarButtonCreator(new TopBarButtonCreatorMock())
20 28
                 .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
21 29
                 .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
22
-                .setTopBarController(new TopBarController())
30
+                .setTopBarController(new TopBarController() {
31
+                    @Override
32
+                    protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout, ImageLoader imageLoader) {
33
+                        TopBar topBar = super.createTopBar(context, buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout, imageLoader);
34
+                        topBar.layout(0, 0, 1000, 100);
35
+                        return topBar;
36
+                    }
37
+                })
38
+                .setStackPresenter(new StackOptionsPresenter(activity, new Options()))
23 39
                 .setInitialOptions(new Options());
24 40
     }
25 41
 }

+ 2
- 1
lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleComponentViewController.java Просмотреть файл

@@ -3,10 +3,11 @@ package com.reactnativenavigation.mocks;
3 3
 import android.app.*;
4 4
 
5 5
 import com.reactnativenavigation.parse.*;
6
+import com.reactnativenavigation.presentation.OptionsPresenter;
6 7
 import com.reactnativenavigation.viewcontrollers.*;
7 8
 
8 9
 public class SimpleComponentViewController extends ComponentViewController {
9 10
     public SimpleComponentViewController(Activity activity, ChildControllersRegistry childRegistry, String id, Options initialOptions) {
10
-        super(activity, childRegistry,id, "theComponentName", new TestComponentViewCreator(), initialOptions);
11
+        super(activity, childRegistry,id, "theComponentName", new TestComponentViewCreator(), initialOptions, new OptionsPresenter(activity, new Options()));
11 12
     }
12 13
 }

+ 15
- 3
lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleViewController.java Просмотреть файл

@@ -6,10 +6,12 @@ import android.support.annotation.NonNull;
6 6
 import android.view.MotionEvent;
7 7
 import android.view.View;
8 8
 import android.widget.FrameLayout;
9
+import android.widget.RelativeLayout;
9 10
 
10 11
 import com.reactnativenavigation.interfaces.ScrollEventListener;
11 12
 import com.reactnativenavigation.parse.Options;
12 13
 import com.reactnativenavigation.presentation.OptionsPresenter;
14
+import com.reactnativenavigation.utils.ViewUtils;
13 15
 import com.reactnativenavigation.viewcontrollers.ChildController;
14 16
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
15 17
 import com.reactnativenavigation.views.ReactComponent;
@@ -20,7 +22,7 @@ public class SimpleViewController extends ChildController<SimpleViewController.S
20 22
     private SimpleView simpleView;
21 23
 
22 24
     public SimpleViewController(Activity activity, ChildControllersRegistry childRegistry, String id, Options options) {
23
-        this(activity, childRegistry, id, new OptionsPresenter(activity), options);
25
+        this(activity, childRegistry, id, new OptionsPresenter(activity, new Options()), options);
24 26
     }
25 27
 
26 28
     public SimpleViewController(Activity activity, ChildControllersRegistry childRegistry, String id, OptionsPresenter presenter, Options options) {
@@ -57,12 +59,22 @@ public class SimpleViewController extends ChildController<SimpleViewController.S
57 59
 
58 60
         @Override
59 61
         public void drawBehindTopBar() {
60
-
62
+            if (getLayoutParams() instanceof RelativeLayout.LayoutParams) {
63
+                RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
64
+                if (layoutParams.topMargin == 0) return;
65
+                layoutParams.topMargin = 0;
66
+                setLayoutParams(layoutParams);
67
+            }
61 68
         }
62 69
 
63 70
         @Override
64 71
         public void drawBelowTopBar(TopBar topBar) {
65
-
72
+            if (getLayoutParams() instanceof RelativeLayout.LayoutParams) {
73
+                RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
74
+                if (layoutParams.topMargin == ViewUtils.getPreferredHeight(topBar)) return;
75
+                layoutParams.topMargin = ViewUtils.getPreferredHeight(topBar);
76
+//                setLayoutParams(layoutParams);
77
+            }
66 78
         }
67 79
 
68 80
         @Override

+ 2
- 2
lib/android/app/src/test/java/com/reactnativenavigation/parse/OptionsTest.java Просмотреть файл

@@ -269,9 +269,9 @@ public class OptionsTest extends BaseTest {
269 269
     @Test
270 270
     public void clear_bottomTabsOptions() {
271 271
         Options uut = new Options();
272
-        uut.bottomTabsOptions.tabColor = new com.reactnativenavigation.parse.params.Color(android.graphics.Color.RED);
272
+        uut.bottomTabsOptions.backgroundColor = new com.reactnativenavigation.parse.params.Color(android.graphics.Color.RED);
273 273
         uut.clearBottomTabsOptions();
274
-        assertThat(uut.bottomTabsOptions.tabColor.hasValue()).isFalse();
274
+        assertThat(uut.bottomTabsOptions.backgroundColor.hasValue()).isFalse();
275 275
     }
276 276
 
277 277
     @Test

+ 1
- 1
lib/android/app/src/test/java/com/reactnativenavigation/utils/OptionHelper.java Просмотреть файл

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

+ 96
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabOptionsPresenterTest.java Просмотреть файл

@@ -0,0 +1,96 @@
1
+package com.reactnativenavigation.viewcontrollers;
2
+
3
+import android.app.Activity;
4
+
5
+import com.reactnativenavigation.BaseTest;
6
+import com.reactnativenavigation.mocks.SimpleViewController;
7
+import com.reactnativenavigation.parse.Options;
8
+import com.reactnativenavigation.parse.params.Color;
9
+import com.reactnativenavigation.parse.params.Text;
10
+import com.reactnativenavigation.presentation.BottomTabOptionsPresenter;
11
+import com.reactnativenavigation.views.BottomTabs;
12
+import com.reactnativenavigation.views.Component;
13
+
14
+import org.junit.Test;
15
+import org.mockito.Mockito;
16
+
17
+import java.util.Arrays;
18
+import java.util.List;
19
+
20
+import static org.mockito.ArgumentMatchers.anyInt;
21
+import static org.mockito.ArgumentMatchers.anyString;
22
+import static org.mockito.ArgumentMatchers.eq;
23
+import static org.mockito.Mockito.spy;
24
+import static org.mockito.Mockito.times;
25
+import static org.mockito.Mockito.verify;
26
+import static org.mockito.Mockito.verifyNoMoreInteractions;
27
+
28
+public class BottomTabOptionsPresenterTest extends BaseTest {
29
+    private Options tab1Options = createTab1Options();
30
+    private Options tab2Options = createTab2Options();
31
+    private BottomTabOptionsPresenter uut;
32
+    private BottomTabs bottomTabs;
33
+    private List<ViewController> tabs;
34
+    private ViewController child3;
35
+
36
+    @Override
37
+    public void beforeEach() {
38
+        Activity activity = newActivity();
39
+        ChildControllersRegistry childRegistry = new ChildControllersRegistry();
40
+        bottomTabs = Mockito.mock(BottomTabs.class);
41
+        ViewController child1 = spy(new SimpleViewController(activity, childRegistry, "child1", tab1Options));
42
+        ViewController child2 = spy(new SimpleViewController(activity, childRegistry, "child2", tab2Options));
43
+        child3 = spy(new SimpleViewController(activity, childRegistry, "child2", new Options()));
44
+        tabs = Arrays.asList(child1, child2, child3);
45
+        uut = new BottomTabOptionsPresenter(activity, tabs, new Options());
46
+        uut.bindView(bottomTabs);
47
+        uut.setDefaultOptions(new Options());
48
+    }
49
+
50
+    @Test
51
+    public void present() {
52
+        uut.present();
53
+        for (int i = 0; i < tabs.size(); i++) {
54
+            verify(bottomTabs, times(1)).setBadge(i, tabs.get(i).options.bottomTabOptions.badge.get(""));
55
+            verify(bottomTabs, times(1)).setTitleInactiveColor(i, tabs.get(i).options.bottomTabOptions.textColor.get(null));
56
+            verify(bottomTabs, times(1)).setTitleActiveColor(i, tabs.get(i).options.bottomTabOptions.selectedTextColor.get(null));
57
+        }
58
+    }
59
+
60
+    @Test
61
+    public void mergeChildOptions() {
62
+        for (int i = 0; i < 2; i++) {
63
+            Options options = tabs.get(i).options;
64
+            uut.mergeChildOptions(options, (Component) tabs.get(i).getView());
65
+            verify(bottomTabs, times(1)).setBadge(i, options.bottomTabOptions.badge.get());
66
+            verify(bottomTabs, times(1)).setIconActiveColor(eq(i), anyInt());
67
+            verify(bottomTabs, times(1)).setIconInactiveColor(eq(i), anyInt());
68
+        }
69
+        verifyNoMoreInteractions(bottomTabs);
70
+    }
71
+
72
+    @Test
73
+    public void mergeChildOptions_onlySetsDefinedOptions() {
74
+        uut.mergeChildOptions(child3.options, (Component) child3.getView());
75
+        verify(bottomTabs, times(0)).setBadge(eq(2), anyString());
76
+        verify(bottomTabs, times(0)).setIconInactiveColor(eq(2), anyInt());
77
+        verify(bottomTabs, times(0)).setIconActiveColor(eq(2), anyInt());
78
+        verifyNoMoreInteractions(bottomTabs);
79
+    }
80
+
81
+    private Options createTab1Options() {
82
+        Options options = new Options();
83
+        options.bottomTabOptions.badge = new Text("tab1badge");
84
+        options.bottomTabOptions.iconColor = new Color(android.graphics.Color.RED);
85
+        options.bottomTabOptions.selectedIconColor = new Color(android.graphics.Color.RED);
86
+        return options;
87
+    }
88
+
89
+    private Options createTab2Options() {
90
+        Options options = new Options();
91
+        options.bottomTabOptions.badge = new Text("tab2badge");
92
+        options.bottomTabOptions.iconColor = new Color(android.graphics.Color.RED);
93
+        options.bottomTabOptions.selectedIconColor = new Color(android.graphics.Color.RED);
94
+        return options;
95
+    }
96
+}

+ 67
- 26
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsControllerTest.java Просмотреть файл

@@ -15,6 +15,10 @@ import com.reactnativenavigation.parse.params.Bool;
15 15
 import com.reactnativenavigation.parse.params.Color;
16 16
 import com.reactnativenavigation.parse.params.Number;
17 17
 import com.reactnativenavigation.parse.params.Text;
18
+import com.reactnativenavigation.presentation.BottomTabOptionsPresenter;
19
+import com.reactnativenavigation.presentation.BottomTabsOptionsPresenter;
20
+import com.reactnativenavigation.presentation.OptionsPresenter;
21
+import com.reactnativenavigation.presentation.StackOptionsPresenter;
18 22
 import com.reactnativenavigation.react.EventEmitter;
19 23
 import com.reactnativenavigation.utils.CommandListenerAdapter;
20 24
 import com.reactnativenavigation.utils.ImageLoader;
@@ -33,6 +37,7 @@ import java.util.List;
33 37
 
34 38
 import static org.assertj.core.api.Java6Assertions.assertThat;
35 39
 import static org.mockito.ArgumentMatchers.any;
40
+import static org.mockito.ArgumentMatchers.eq;
36 41
 import static org.mockito.Mockito.spy;
37 42
 import static org.mockito.Mockito.times;
38 43
 import static org.mockito.Mockito.verify;
@@ -47,11 +52,13 @@ public class BottomTabsControllerTest extends BaseTest {
47 52
     private ViewController child3;
48 53
     private StackController child4;
49 54
     private ViewController child5;
55
+    private ViewController child6;
50 56
     private Options tabOptions = OptionHelper.createBottomTabOptions();
51 57
     private ImageLoader imageLoaderMock = ImageLoaderMock.mock();
52 58
     private EventEmitter eventEmitter;
53 59
     private ChildControllersRegistry childRegistry;
54 60
     private List<ViewController> tabs;
61
+    private BottomTabsOptionsPresenter presenter;
55 62
 
56 63
     @Override
57 64
     public void beforeEach() {
@@ -64,17 +71,11 @@ public class BottomTabsControllerTest extends BaseTest {
64 71
         child3 = spy(new SimpleViewController(activity, childRegistry, "child3", tabOptions));
65 72
         child4 = spy(createStack("someStack"));
66 73
         child5 = spy(new SimpleViewController(activity, childRegistry, "child5", tabOptions));
74
+        child6 = spy(new SimpleViewController(activity, childRegistry, "child6", tabOptions));
67 75
         when(child5.handleBack(any())).thenReturn(true);
68 76
         tabs = createTabs();
69
-
70
-        uut = new BottomTabsController(activity, tabs, childRegistry, eventEmitter, imageLoaderMock, "uut", new Options()) {
71
-            @Override
72
-            public void ensureViewIsCreated() {
73
-                super.ensureViewIsCreated();
74
-                uut.getView().layout(0, 0, 1000, 1000);
75
-                uut.getBottomTabs().layout(0, 0, 1000, 100);
76
-            }
77
-        };
77
+        presenter = spy(new BottomTabsOptionsPresenter(tabs, new Options()));
78
+        uut = createBottomTabs();
78 79
     }
79 80
 
80 81
     @Test
@@ -86,21 +87,7 @@ public class BottomTabsControllerTest extends BaseTest {
86 87
     @Test(expected = RuntimeException.class)
87 88
     public void setTabs_ThrowWhenMoreThan5() {
88 89
         tabs.add(new SimpleViewController(activity, childRegistry, "6", tabOptions));
89
-        new BottomTabsController(activity, tabs, childRegistry, eventEmitter, imageLoaderMock, "uut", new Options()) {
90
-            @Override
91
-            public void ensureViewIsCreated() {
92
-                super.ensureViewIsCreated();
93
-                uut.getView().layout(0, 0, 1000, 1000);
94
-                uut.getBottomTabs().layout(0, 0, 1000, 100);
95
-            }
96
-        };
97
-    }
98
-
99
-    @Test
100
-    public void setTab_controllerIsSetAsParent() {
101
-        for (ViewController tab : tabs) {
102
-            assertThat(tab.getParentController()).isEqualTo(uut);
103
-        }
90
+        createBottomTabs();
104 91
     }
105 92
 
106 93
     @Test
@@ -120,8 +107,19 @@ public class BottomTabsControllerTest extends BaseTest {
120 107
         }
121 108
     }
122 109
 
110
+    @Test
111
+    public void createView_layoutOptionsAreAppliedToTabs() {
112
+        uut.ensureViewIsCreated();
113
+        for (int i = 0; i < tabs.size(); i++) {
114
+            verify(presenter, times(1)).applyLayoutParamsOptions(any(), eq(i));
115
+            assertThat(childLayoutParams(i).width).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT);
116
+            assertThat(childLayoutParams(i).height).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT);
117
+        }
118
+    }
119
+
123 120
     @Test
124 121
     public void onTabSelected() {
122
+        uut.ensureViewIsCreated();
125 123
         assertThat(uut.getSelectedIndex()).isZero();
126 124
         assertThat(((ViewController) ((List) uut.getChildControllers()).get(0)).getView().getVisibility()).isEqualTo(View.VISIBLE);
127 125
 
@@ -135,6 +133,7 @@ public class BottomTabsControllerTest extends BaseTest {
135 133
 
136 134
     @Test
137 135
     public void onTabReSelected() {
136
+        uut.ensureViewIsCreated();
138 137
         assertThat(uut.getSelectedIndex()).isZero();
139 138
 
140 139
         uut.onTabSelected(0, false);
@@ -146,6 +145,7 @@ public class BottomTabsControllerTest extends BaseTest {
146 145
 
147 146
     @Test
148 147
     public void handleBack_DelegatesToSelectedChild() {
148
+        uut.ensureViewIsCreated();
149 149
         assertThat(uut.handleBack(new CommandListenerAdapter())).isFalse();
150 150
         uut.selectTab(4);
151 151
         assertThat(uut.handleBack(new CommandListenerAdapter())).isTrue();
@@ -155,7 +155,7 @@ public class BottomTabsControllerTest extends BaseTest {
155 155
     @Test
156 156
     public void applyOptions_bottomTabsOptionsAreClearedAfterApply() {
157 157
         Options options = new Options();
158
-        options.bottomTabsOptions.tabColor = new Color(android.graphics.Color.RED);
158
+        options.bottomTabsOptions.backgroundColor = new Color(android.graphics.Color.RED);
159 159
         child1.mergeOptions(options);
160 160
         uut.ensureViewIsCreated();
161 161
 
@@ -168,7 +168,7 @@ public class BottomTabsControllerTest extends BaseTest {
168 168
         ArgumentCaptor<ReactComponent> viewCaptor = ArgumentCaptor.forClass(ReactComponent.class);
169 169
         verify(stack, times(1)).applyChildOptions(optionsCaptor.capture(), viewCaptor.capture());
170 170
         assertThat(viewCaptor.getValue()).isEqualTo(child1.getView());
171
-        assertThat(optionsCaptor.getValue().bottomTabsOptions.tabColor.hasValue()).isFalse();
171
+        assertThat(optionsCaptor.getValue().bottomTabsOptions.backgroundColor.hasValue()).isFalse();
172 172
     }
173 173
 
174 174
     @Test
@@ -226,6 +226,7 @@ public class BottomTabsControllerTest extends BaseTest {
226 226
 
227 227
     @Test
228 228
     public void push() {
229
+        uut.ensureViewIsCreated();
229 230
         uut.selectTab(3);
230 231
 
231 232
         SimpleViewController stackChild = new SimpleViewController(activity, childRegistry, "stackChild", new Options());
@@ -238,6 +239,25 @@ public class BottomTabsControllerTest extends BaseTest {
238 239
         assertThat(child4.size()).isEqualTo(2);
239 240
     }
240 241
 
242
+    @Test
243
+    public void deepChildOptionsAreApplied() {
244
+        BottomTabsController spy = spy(uut);
245
+        activity.setContentView(spy.getView());
246
+
247
+        child6.options.topBar.drawBehind = new Bool(false);
248
+        disablePushAnimation(child6);
249
+        child4.push(child6, new CommandListenerAdapter());
250
+        assertThat(child4.size()).isOne();
251
+
252
+
253
+        verify(spy, times(1)).onViewAppeared();
254
+        assertThat(spy.getSelectedIndex()).isZero();
255
+        verify(child6, times(0)).onViewAppeared();
256
+        assertThat(child4.getTopBar().getHeight())
257
+                .isNotZero()
258
+                .isEqualTo(((ViewGroup.MarginLayoutParams) child6.getView().getLayoutParams()).topMargin);
259
+    }
260
+
241 261
     @NonNull
242 262
     private List<ViewController> createTabs() {
243 263
         return Arrays.asList(child1, child2, child3, child4, child5);
@@ -247,10 +267,31 @@ public class BottomTabsControllerTest extends BaseTest {
247 267
         return TestUtils.newStackController(activity)
248 268
                 .setId(id)
249 269
                 .setInitialOptions(tabOptions)
270
+                .setStackPresenter(new StackOptionsPresenter(activity, new Options()))
250 271
                 .build();
251 272
     }
252 273
 
253 274
     private ViewGroup.MarginLayoutParams childLayoutParams(int index) {
254 275
         return (ViewGroup.MarginLayoutParams) tabs.get(index).getView().getLayoutParams();
255 276
     }
277
+
278
+    public BottomTabsController createBottomTabs() {
279
+        return new BottomTabsController(activity,
280
+                tabs,
281
+                childRegistry,
282
+                eventEmitter,
283
+                imageLoaderMock,
284
+                "uut",
285
+                new Options(),
286
+                new OptionsPresenter(activity, new Options()),
287
+                presenter,
288
+                new BottomTabOptionsPresenter(activity, tabs, new Options())) {
289
+            @Override
290
+            public void ensureViewIsCreated() {
291
+                super.ensureViewIsCreated();
292
+                uut.getView().layout(0, 0, 1000, 1000);
293
+                uut.getBottomTabs().layout(0, 0, 1000, 100);
294
+            }
295
+        };
296
+    }
256 297
 }

+ 3
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ComponentViewControllerTest.java Просмотреть файл

@@ -7,6 +7,7 @@ import com.reactnativenavigation.TestUtils;
7 7
 import com.reactnativenavigation.mocks.TestComponentLayout;
8 8
 import com.reactnativenavigation.mocks.TestReactView;
9 9
 import com.reactnativenavigation.parse.Options;
10
+import com.reactnativenavigation.presentation.OptionsPresenter;
10 11
 import com.reactnativenavigation.views.StackLayout;
11 12
 
12 13
 import org.junit.Test;
@@ -27,7 +28,8 @@ public class ComponentViewControllerTest extends BaseTest {
27 28
         Activity activity = newActivity();
28 29
         view = spy(new TestComponentLayout(activity, new TestReactView(activity)));
29 30
         ParentController<StackLayout> parentController = TestUtils.newStackController(activity).build();
30
-        uut = new ComponentViewController(activity, new ChildControllersRegistry(), "componentId1", "componentName", (activity1, componentId, componentName) -> view, new Options());
31
+        OptionsPresenter presenter = new OptionsPresenter(activity, new Options());
32
+        uut = new ComponentViewController(activity, new ChildControllersRegistry(), "componentId1", "componentName", (activity1, componentId, componentName) -> view, new Options(), presenter);
31 33
         uut.setParentController(parentController);
32 34
         parentController.ensureViewIsCreated();
33 35
     }

+ 1
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/FloatingActionButtonTest.java Просмотреть файл

@@ -37,6 +37,7 @@ public class FloatingActionButtonTest extends BaseTest {
37 37
         activity = newActivity();
38 38
         childRegistry = new ChildControllersRegistry();
39 39
         stackController = TestUtils.newStackController(activity).build();
40
+        stackController.ensureViewIsCreated();
40 41
         Options options = getOptionsWithFab();
41 42
         childFab = new SimpleViewController(activity, childRegistry, "child1", options);
42 43
         childNoFab = new SimpleViewController(activity, childRegistry, "child2", new Options());

+ 64
- 28
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java Просмотреть файл

@@ -1,6 +1,7 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3 3
 import android.support.annotation.NonNull;
4
+import android.view.View;
4 5
 
5 6
 import com.reactnativenavigation.BaseTest;
6 7
 import com.reactnativenavigation.TestActivity;
@@ -11,6 +12,9 @@ import com.reactnativenavigation.mocks.SimpleViewController;
11 12
 import com.reactnativenavigation.parse.Options;
12 13
 import com.reactnativenavigation.parse.params.Bool;
13 14
 import com.reactnativenavigation.parse.params.Text;
15
+import com.reactnativenavigation.presentation.BottomTabOptionsPresenter;
16
+import com.reactnativenavigation.presentation.BottomTabsOptionsPresenter;
17
+import com.reactnativenavigation.presentation.OptionsPresenter;
14 18
 import com.reactnativenavigation.presentation.OverlayManager;
15 19
 import com.reactnativenavigation.react.EventEmitter;
16 20
 import com.reactnativenavigation.utils.CommandListener;
@@ -50,6 +54,7 @@ public class NavigatorTest extends BaseTest {
50 54
     private ActivityController<TestActivity> activityController;
51 55
     private OverlayManager overlayManager;
52 56
     private EventEmitter eventEmitter;
57
+    private ViewController.ViewVisibilityListener parentVisibilityListener;
53 58
 
54 59
     @Override
55 60
     public void beforeEach() {
@@ -62,8 +67,20 @@ public class NavigatorTest extends BaseTest {
62 67
         uut = new Navigator(activity, childRegistry, overlayManager);
63 68
         activity.setNavigator(uut);
64 69
 
65
-        parentController = spy(newStack());
66
-        parentController.ensureViewIsCreated();
70
+        parentController = newStack();
71
+        parentVisibilityListener = spy(new ViewController.ViewVisibilityListener() {
72
+            @Override
73
+            public boolean onViewAppeared(View view) {
74
+                return false;
75
+            }
76
+
77
+            @Override
78
+            public boolean onViewDisappear(View view) {
79
+                return false;
80
+            }
81
+        });
82
+        parentController.setViewVisibilityListener(parentVisibilityListener);
83
+//        parentController.ensureViewIsCreated();
67 84
         child1 = new SimpleViewController(activity, childRegistry, "child1", tabOptions);
68 85
         child2 = new SimpleViewController(activity, childRegistry, "child2", tabOptions);
69 86
         child3 = new SimpleViewController(activity, childRegistry, "child3", tabOptions);
@@ -74,6 +91,17 @@ public class NavigatorTest extends BaseTest {
74 91
         activityController.visible();
75 92
     }
76 93
 
94
+    @Test
95
+    public void setDefaultOptions() {
96
+        uut.setDefaultOptions(new Options());
97
+
98
+        SimpleViewController spy = spy(child1);
99
+        uut.setRoot(spy, new CommandListenerAdapter());
100
+        Options defaultOptions = new Options();
101
+        uut.setDefaultOptions(defaultOptions);
102
+        verify(spy, times(1)).setDefaultOptions(defaultOptions);
103
+    }
104
+
77 105
     @Test
78 106
     public void setRoot_AddsChildControllerView() {
79 107
         assertThat(uut.getContentLayout().getChildCount()).isZero();
@@ -118,8 +146,8 @@ public class NavigatorTest extends BaseTest {
118 146
 
119 147
     @Test
120 148
     public void push_OnCorrectStackByFindingChildId() {
121
-        StackController stack1 = newStack();
122
-        StackController stack2 = newStack();
149
+        StackController stack1 = newStack(); stack1.ensureViewIsCreated();
150
+        StackController stack2 = newStack(); stack2.ensureViewIsCreated();
123 151
         stack1.push(child1, new CommandListenerAdapter());
124 152
         stack2.push(child2, new CommandListenerAdapter());
125 153
         BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
@@ -165,8 +193,9 @@ public class NavigatorTest extends BaseTest {
165 193
 
166 194
     @Test
167 195
     public void popSpecific() {
168
-        StackController stack1 = newStack();
169
-        StackController stack2 = newStack();
196
+        disablePushAnimation(child1, child2, child3, child4);
197
+        StackController stack1 = newStack(); stack1.ensureViewIsCreated();
198
+        StackController stack2 = newStack(); stack2.ensureViewIsCreated();
170 199
         stack1.push(child1, new CommandListenerAdapter());
171 200
         stack2.push(child2, new CommandListenerAdapter());
172 201
         stack2.push(child3, new CommandListenerAdapter());
@@ -271,16 +300,18 @@ public class NavigatorTest extends BaseTest {
271 300
 
272 301
     @NonNull
273 302
     private BottomTabsController newTabs(List<ViewController> tabs) {
274
-        return new BottomTabsController(activity, tabs, childRegistry, eventEmitter, imageLoaderMock, "tabsController", new Options());
303
+        return new BottomTabsController(activity, tabs, childRegistry, eventEmitter, imageLoaderMock, "tabsController", new Options(), new OptionsPresenter(activity, new Options()), new BottomTabsOptionsPresenter(tabs, new Options()), new BottomTabOptionsPresenter(activity, tabs, new Options()));
275 304
     }
276 305
 
277 306
     @NonNull
278 307
     private StackController newStack() {
279
-        return TestUtils.newStackController(activity)
308
+        StackController stack = TestUtils.newStackController(activity)
280 309
                 .setChildRegistry(childRegistry)
281 310
                 .setId("stack" + CompatUtils.generateViewId())
282 311
                 .setInitialOptions(tabOptions)
283 312
                 .build();
313
+        stack.ensureViewIsCreated();
314
+        return stack;
284 315
     }
285 316
 
286 317
     @Test
@@ -358,25 +389,26 @@ public class NavigatorTest extends BaseTest {
358 389
     public void pushedStackCanBePopped() {
359 390
         child1.options.animations.push.enable = new Bool(false);
360 391
         child2.options.animations.push.enable = new Bool(false);
392
+        StackController spy = spy(parentController);
361 393
         StackController parent = newStack();
362 394
         parent.ensureViewIsCreated();
363 395
         uut.setRoot(parent, new CommandListenerAdapter());
364
-        parent.push(parentController, new CommandListenerAdapter());
396
+        parent.push(spy, new CommandListenerAdapter());
365 397
 
366
-        parentController.push(child1, new CommandListenerAdapter());
367
-        parentController.push(child2, new CommandListenerAdapter());
368
-        assertThat(parentController.getChildControllers().size()).isEqualTo(2);
398
+        spy.push(child1, new CommandListenerAdapter());
399
+        spy.push(child2, new CommandListenerAdapter());
400
+        assertThat(spy.getChildControllers().size()).isEqualTo(2);
369 401
         child1.ensureViewIsCreated();
370 402
         child2.ensureViewIsCreated();
371 403
 
372 404
         CommandListenerAdapter listener = new CommandListenerAdapter() {
373 405
             @Override
374 406
             public void onSuccess(String childId) {
375
-                assertThat(parentController.getChildControllers().size()).isEqualTo(1);
407
+                assertThat(spy.getChildControllers().size()).isEqualTo(1);
376 408
             }
377 409
         };
378 410
         uut.popSpecific("child2", listener);
379
-        verify(parentController, times(1)).popSpecific(child2, listener);
411
+        verify(spy, times(1)).popSpecific(child2, listener);
380 412
     }
381 413
 
382 414
     @Test
@@ -406,12 +438,12 @@ public class NavigatorTest extends BaseTest {
406 438
 
407 439
         uut.dismissModal(child2.getId(), new CommandListenerAdapter());
408 440
         assertThat(parentController.getView().getParent()).isNull();
409
-        verify(parentController, times(1)).onViewAppeared();
441
+        verify(parentVisibilityListener, times(1)).onViewAppeared(parentController.getView());
410 442
 
411 443
         uut.dismissModal(child1.getId(), new CommandListenerAdapter());
412 444
         assertThat(parentController.getView().getParent()).isNotNull();
413 445
 
414
-        verify(parentController, times(2)).onViewAppeared();
446
+        verify(parentVisibilityListener, times(2)).onViewAppeared(parentController.getView());
415 447
     }
416 448
 
417 449
     @Test
@@ -419,42 +451,45 @@ public class NavigatorTest extends BaseTest {
419 451
         disableShowModalAnimation(child1);
420 452
 
421 453
         uut.dismissAllModals(new CommandListenerAdapter());
422
-        verify(parentController, times(0)).onViewAppeared();
454
+        verify(parentVisibilityListener, times(0)).onViewAppeared(parentController.getView());
423 455
 
424 456
         uut.setRoot(parentController, new CommandListenerAdapter());
425
-        verify(parentController, times(1)).onViewAppeared();
457
+
458
+        verify(parentVisibilityListener, times(1)).onViewAppeared(parentController.getView());
426 459
         uut.showModal(child1, new CommandListenerAdapter());
427 460
         uut.dismissAllModals(new CommandListenerAdapter());
428 461
 
429
-        verify(parentController, times(2)).onViewAppeared();
462
+        verify(parentVisibilityListener, times(2)).onViewAppeared(parentController.getView());
430 463
     }
431 464
 
432 465
     @Test
433 466
     public void handleBack_onViewAppearedInvokedOnRoot() {
434 467
         disableShowModalAnimation(child1, child2);
435 468
 
436
-        uut.setRoot(parentController, new CommandListenerAdapter());
469
+        StackController spy = spy(parentController);
470
+        uut.setRoot(spy, new CommandListenerAdapter());
437 471
         uut.showModal(child1, new CommandListenerAdapter());
438 472
         uut.showModal(child2, new CommandListenerAdapter());
439 473
 
440 474
         uut.handleBack(new CommandListenerAdapter());
441
-        verify(parentController, times(1)).onViewAppeared();
475
+        verify(parentVisibilityListener, times(1)).onViewAppeared(spy.getView());
442 476
 
443 477
         uut.handleBack(new CommandListenerAdapter() {
444 478
             @Override
445 479
             public void onSuccess(String childId) {
446
-                assertThat(parentController.getView().getParent()).isNotNull();
447
-                verify(parentController, times(2)).onViewAppeared();
480
+                assertThat(spy.getView().getParent()).isNotNull();
481
+                verify(spy, times(2)).onViewAppeared();
448 482
             }
449 483
         });
450 484
     }
451 485
 
452 486
     @Test
453 487
     public void destroy_destroyedRoot() {
454
-        parentController.options.animations.startApp.enable = new Bool(false);
455
-        uut.setRoot(parentController, new CommandListenerAdapter());
488
+        StackController spy = spy(parentController);
489
+        spy.options.animations.startApp.enable = new Bool(false);
490
+        uut.setRoot(spy, new CommandListenerAdapter());
456 491
         activityController.destroy();
457
-        verify(parentController, times(1)).destroy();
492
+        verify(spy, times(1)).destroy();
458 493
     }
459 494
 
460 495
     @Test
@@ -476,9 +511,10 @@ public class NavigatorTest extends BaseTest {
476 511
 
477 512
     @Test
478 513
     public void reload_navigatorIsDestroyedOnReload() {
479
-        uut.setRoot(parentController, new CommandListenerAdapter());
514
+        StackController spy = spy(parentController);
515
+        uut.setRoot(spy, new CommandListenerAdapter());
480 516
         uut.onReload();
481
-        verify(parentController, times(1)).destroy();
517
+        verify(spy, times(1)).destroy();
482 518
         verify(overlayManager, times(1)).destroy();
483 519
     }
484 520
 }

+ 7
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java Просмотреть файл

@@ -21,6 +21,8 @@ import com.reactnativenavigation.parse.TopBarBackgroundOptions;
21 21
 import com.reactnativenavigation.parse.params.Bool;
22 22
 import com.reactnativenavigation.parse.params.Fraction;
23 23
 import com.reactnativenavigation.parse.params.Text;
24
+import com.reactnativenavigation.presentation.OptionsPresenter;
25
+import com.reactnativenavigation.presentation.StackOptionsPresenter;
24 26
 import com.reactnativenavigation.utils.CommandListenerAdapter;
25 27
 import com.reactnativenavigation.utils.ImageLoader;
26 28
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
@@ -62,7 +64,8 @@ public class OptionsApplyingTest extends BaseTest {
62 64
                 "componentId1",
63 65
                 "componentName",
64 66
                 (activity1, componentId, componentName) -> view,
65
-                initialNavigationOptions
67
+                initialNavigationOptions,
68
+                new OptionsPresenter(activity, new Options())
66 69
         );
67 70
         TopBarController topBarController = new TopBarController() {
68 71
             @Override
@@ -102,7 +105,9 @@ public class OptionsApplyingTest extends BaseTest {
102 105
                         .setTopBarController(new TopBarController())
103 106
                         .setId("stackId")
104 107
                         .setInitialOptions(new Options())
108
+                        .setStackPresenter(new StackOptionsPresenter(activity, new Options()))
105 109
                         .build();
110
+        stackController.ensureViewIsCreated();
106 111
         stackController.push(uut, new CommandListenerAdapter());
107 112
         assertThat(stackController.getTopBar().getTitle()).isEmpty();
108 113
 
@@ -162,6 +167,7 @@ public class OptionsApplyingTest extends BaseTest {
162 167
         });
163 168
     }
164 169
 
170
+    @SuppressWarnings("MagicNumber")
165 171
     @Test
166 172
     public void appliesTopBarTextSize() {
167 173
         assertThat(uut.initialOptions).isSameAs(initialNavigationOptions);

+ 8
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ParentControllerTest.java Просмотреть файл

@@ -45,9 +45,14 @@ public class ParentControllerTest extends BaseTest {
45 45
         children = new ArrayList<>();
46 46
         Options initialOptions = new Options();
47 47
         initialOptions.topBar.title.text = new Text(INITIAL_TITLE);
48
-        presenter = spy(new OptionsPresenter(activity));
48
+        presenter = spy(new OptionsPresenter(activity, new Options()));
49 49
         uut = spy(new ParentController(activity, childRegistry, "uut", presenter, initialOptions) {
50 50
 
51
+            @Override
52
+            protected ViewController getCurrentChild() {
53
+                return children.get(0);
54
+            }
55
+
51 56
             @NonNull
52 57
             @Override
53 58
             protected ViewGroup createView() {
@@ -96,6 +101,7 @@ public class ParentControllerTest extends BaseTest {
96 101
     @Test
97 102
     public void findControllerById_Recursive() {
98 103
         StackController stackController = TestUtils.newStackController(activity).build();
104
+        stackController.ensureViewIsCreated();
99 105
         SimpleViewController child1 = new SimpleViewController(activity, childRegistry, "child1", new Options());
100 106
         SimpleViewController child2 = new SimpleViewController(activity, childRegistry, "child2", new Options());
101 107
         stackController.push(child1, new CommandListenerAdapter());
@@ -118,6 +124,7 @@ public class ParentControllerTest extends BaseTest {
118 124
     @Test
119 125
     public void optionsAreClearedWhenChildIsAppeared() {
120 126
         StackController stackController = spy(TestUtils.newStackController(activity).build());
127
+        stackController.ensureViewIsCreated();
121 128
         SimpleViewController child1 = new SimpleViewController(activity, childRegistry, "child1", new Options());
122 129
         stackController.push(child1, new CommandListenerAdapter());
123 130
 

+ 3
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/SideMenuControllerTest.java Просмотреть файл

@@ -7,6 +7,7 @@ import com.reactnativenavigation.BaseTest;
7 7
 import com.reactnativenavigation.mocks.SimpleComponentViewController;
8 8
 import com.reactnativenavigation.parse.Options;
9 9
 import com.reactnativenavigation.parse.params.Bool;
10
+import com.reactnativenavigation.presentation.OptionsPresenter;
10 11
 
11 12
 import org.junit.Test;
12 13
 
@@ -21,7 +22,8 @@ public class SideMenuControllerTest extends BaseTest {
21 22
     public void beforeEach() {
22 23
         activity = newActivity();
23 24
         childRegistry = new ChildControllersRegistry();
24
-        uut = new SideMenuController(activity, childRegistry, "sideMenu", new Options());
25
+        OptionsPresenter presenter = new OptionsPresenter(activity, new Options());
26
+        uut = new SideMenuController(activity, childRegistry, "sideMenu", new Options(), presenter);
25 27
     }
26 28
 
27 29
     @Test

lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsMergingTest.java → lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackOptionsPresenterTest.java Просмотреть файл

@@ -33,7 +33,7 @@ import static org.mockito.Mockito.times;
33 33
 import static org.mockito.Mockito.verify;
34 34
 import static org.mockito.Mockito.when;
35 35
 
36
-public class OptionsMergingTest extends BaseTest {
36
+public class StackOptionsPresenterTest extends BaseTest {
37 37
 
38 38
     private StackOptionsPresenter uut;
39 39
     private TestComponentLayout child;
@@ -43,8 +43,9 @@ public class OptionsMergingTest extends BaseTest {
43 43
     @Override
44 44
     public void beforeEach() {
45 45
         activity = spy(newActivity());
46
+        uut = spy(new StackOptionsPresenter(activity, new Options()));
46 47
         topBar = mockTopBar();
47
-        uut = spy(new StackOptionsPresenter(topBar));
48
+        uut.bindView(topBar);
48 49
         child = spy(new TestComponentLayout(activity, new TestReactView(activity)));
49 50
     }
50 51
 
@@ -80,16 +81,16 @@ public class OptionsMergingTest extends BaseTest {
80 81
     public void mergeTopBarOptions() {
81 82
         Options options = new Options();
82 83
         uut.mergeChildOptions(options, child);
83
-        assertTopBarOptions(0);
84
-
85
-        TitleOptions titleOptions = new TitleOptions();
86
-        titleOptions.text = new Text("abc");
87
-        titleOptions.component.name = new Text("someComponent");
88
-        titleOptions.component.componentId = new Text("compId");
89
-        titleOptions.color = new Color(0);
90
-        titleOptions.fontSize = new Fraction(1.0f);
91
-        titleOptions.fontFamily = Typeface.DEFAULT_BOLD;
92
-        options.topBar.title = titleOptions;
84
+        assertTopBarOptions(options, 0);
85
+
86
+        TitleOptions title = new TitleOptions();
87
+        title.text = new Text("abc");
88
+        title.component.name = new Text("someComponent");
89
+        title.component.componentId = new Text("compId");
90
+        title.color = new Color(0);
91
+        title.fontSize = new Fraction(1.0f);
92
+        title.fontFamily = Typeface.DEFAULT_BOLD;
93
+        options.topBar.title = title;
93 94
         SubtitleOptions subtitleOptions = new SubtitleOptions();
94 95
         subtitleOptions.text = new Text("Sub");
95 96
         subtitleOptions.color = new Color(1);
@@ -102,7 +103,7 @@ public class OptionsMergingTest extends BaseTest {
102 103
         options.topBar.hideOnScroll = new Bool(false);
103 104
         uut.mergeChildOptions(options, child);
104 105
 
105
-        assertTopBarOptions(1);
106
+        assertTopBarOptions(options, 1);
106 107
 
107 108
         options.topBar.drawBehind = new Bool(true);
108 109
         uut.mergeChildOptions(options, child);
@@ -141,9 +142,14 @@ public class OptionsMergingTest extends BaseTest {
141 142
         verify(topBar, times(1)).setTopTabFontFamily(1, Typeface.DEFAULT_BOLD);
142 143
     }
143 144
 
144
-    private void assertTopBarOptions(int t) {
145
-        verify(topBar, times(t)).setTitle(any());
146
-        verify(topBar, times(t)).setSubtitle(any());
145
+    private void assertTopBarOptions(Options options, int t) {
146
+        if (options.topBar.title.component.hasValue()) {
147
+            verify(topBar, times(0)).setTitle(any());
148
+            verify(topBar, times(0)).setSubtitle(any());
149
+        } else {
150
+            verify(topBar, times(t)).setTitle(any());
151
+            verify(topBar, times(t)).setSubtitle(any());
152
+        }
147 153
         verify(topBar, times(t)).setTitleComponent(any());
148 154
         verify(topBar, times(t)).setBackgroundColor(any());
149 155
         verify(topBar, times(t)).setTitleTextColor(anyInt());

+ 9
- 3
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java Просмотреть файл

@@ -11,6 +11,7 @@ import com.reactnativenavigation.mocks.TestReactView;
11 11
 import com.reactnativenavigation.parse.Options;
12 12
 import com.reactnativenavigation.parse.params.Bool;
13 13
 import com.reactnativenavigation.parse.params.Text;
14
+import com.reactnativenavigation.presentation.OptionsPresenter;
14 15
 import com.reactnativenavigation.utils.CommandListenerAdapter;
15 16
 import com.reactnativenavigation.utils.ViewHelper;
16 17
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
@@ -57,10 +58,12 @@ public class TopTabsViewControllerTest extends BaseTest {
57 58
         topTabsLayout = spy(new TopTabsViewPager(activity, tabControllers, new TopTabsAdapter(tabControllers)));
58 59
         TopTabsLayoutCreator layoutCreator = Mockito.mock(TopTabsLayoutCreator.class);
59 60
         Mockito.when(layoutCreator.create()).thenReturn(topTabsLayout);
60
-        uut = spy(new TopTabsController(activity, childRegistry, "componentId", tabControllers, layoutCreator, options));
61
+        OptionsPresenter presenter = new OptionsPresenter(activity, new Options());
62
+        uut = spy(new TopTabsController(activity, childRegistry, "componentId", tabControllers, layoutCreator, options, presenter));
61 63
         tabControllers.forEach(viewController -> viewController.setParentController(uut));
62 64
 
63 65
         stack = spy(TestUtils.newStackController(activity).build());
66
+        stack.ensureViewIsCreated();
64 67
         stack.push(uut, new CommandListenerAdapter());
65 68
         uut.setParentController(stack);
66 69
     }
@@ -86,7 +89,8 @@ public class TopTabsViewControllerTest extends BaseTest {
86 89
                     "idTab" + i,
87 90
                     "theComponentName",
88 91
                     new TestComponentViewCreator(),
89
-                    tabOptions.get(i)
92
+                    tabOptions.get(i),
93
+                    new OptionsPresenter(activity, new Options())
90 94
             );
91 95
             tabControllers.add(spy(viewController));
92 96
         }
@@ -207,13 +211,15 @@ public class TopTabsViewControllerTest extends BaseTest {
207 211
         stack.getView().removeAllViews();
208 212
 
209 213
         StackController stackController = spy(TestUtils.newStackController(activity).build());
214
+        stackController.ensureViewIsCreated();
210 215
         ComponentViewController first = new ComponentViewController(
211 216
                 activity,
212 217
                 childRegistry,
213 218
                 "firstScreen",
214 219
                 "comp1",
215 220
                 new TestComponentViewCreator(),
216
-                new Options()
221
+                new Options(),
222
+                new OptionsPresenter(activity, new Options())
217 223
         );
218 224
         first.options.animations.push.enable = new Bool(false);
219 225
         uut.options.animations.push.enable = new Bool(false);

+ 1
- 0
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ViewControllerTest.java Просмотреть файл

@@ -75,6 +75,7 @@ public class ViewControllerTest extends BaseTest {
75 75
 
76 76
         assertThat(uut.getParentController()).isNull();
77 77
         StackController nav = TestUtils.newStackController(activity).build();
78
+        nav.ensureViewIsCreated();
78 79
         nav.push(uut, new CommandListenerAdapter());
79 80
         assertThat(uut.getParentController()).isEqualTo(nav);
80 81
     }

+ 9
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/BackButtonHelperTest.java Просмотреть файл

@@ -37,11 +37,12 @@ public class BackButtonHelperTest extends BaseTest {
37 37
                 .build();
38 38
         child1 = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
39 39
         child2 = spy(new SimpleViewController(activity, childRegistry, "child2", new Options()));
40
+        stack.ensureViewIsCreated();
40 41
     }
41 42
 
42 43
     @Test
43 44
     public void addToChild_doesNotAddIfStackContainsOneChild() {
44
-        uut.addToChild(stack, child1);
45
+        uut.addToPushedChild(stack, child1);
45 46
         verify(child1, times(0)).mergeOptions(any());
46 47
     }
47 48
 
@@ -65,4 +66,11 @@ public class BackButtonHelperTest extends BaseTest {
65 66
 
66 67
         verify(child2, times(0)).mergeOptions(any());
67 68
     }
69
+
70
+    @Test
71
+    public void clear() {
72
+        child1.options.topBar.buttons.back.visible = new Bool(true);
73
+        uut.clear(child1);
74
+        assertThat(child1.options.topBar.buttons.back.visible.get()).isFalse();
75
+    }
68 76
 }

+ 78
- 13
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.java Просмотреть файл

@@ -3,6 +3,7 @@ package com.reactnativenavigation.viewcontrollers.stack;
3 3
 import android.app.Activity;
4 4
 import android.content.Context;
5 5
 import android.view.View;
6
+import android.view.ViewGroup;
6 7
 
7 8
 import com.reactnativenavigation.BaseTest;
8 9
 import com.reactnativenavigation.TestUtils;
@@ -17,6 +18,7 @@ import com.reactnativenavigation.parse.Options;
17 18
 import com.reactnativenavigation.parse.params.Bool;
18 19
 import com.reactnativenavigation.parse.params.Button;
19 20
 import com.reactnativenavigation.parse.params.Text;
21
+import com.reactnativenavigation.presentation.StackOptionsPresenter;
20 22
 import com.reactnativenavigation.utils.CommandListenerAdapter;
21 23
 import com.reactnativenavigation.utils.ImageLoader;
22 24
 import com.reactnativenavigation.utils.ViewHelper;
@@ -40,7 +42,9 @@ import org.mockito.ArgumentCaptor;
40 42
 import org.mockito.Mockito;
41 43
 
42 44
 import java.util.ArrayList;
45
+import java.util.Arrays;
43 46
 import java.util.Collections;
47
+import java.util.List;
44 48
 
45 49
 import static org.assertj.core.api.Java6Assertions.assertThat;
46 50
 import static org.mockito.ArgumentMatchers.any;
@@ -61,6 +65,7 @@ public class StackControllerTest extends BaseTest {
61 65
     private ViewController child4;
62 66
     private NavigationAnimator animator;
63 67
     private TopBarController topBarController;
68
+    private StackOptionsPresenter presenter;
64 69
 
65 70
     @Override
66 71
     public void beforeEach() {
@@ -68,11 +73,13 @@ public class StackControllerTest extends BaseTest {
68 73
         animator = Mockito.mock(NavigationAnimator.class);
69 74
         activity = newActivity();
70 75
         childRegistry = new ChildControllersRegistry();
71
-        uut = createStackController();
76
+        presenter = new StackOptionsPresenter(activity, new Options());
72 77
         child1 = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
73 78
         child2 = spy(new SimpleViewController(activity, childRegistry, "child2", new Options()));
74 79
         child3 = spy(new SimpleViewController(activity, childRegistry, "child3", new Options()));
75 80
         child4 = spy(new SimpleViewController(activity, childRegistry, "child4", new Options()));
81
+        uut = createStack();
82
+        uut.ensureViewIsCreated();
76 83
     }
77 84
 
78 85
     @Test
@@ -80,6 +87,22 @@ public class StackControllerTest extends BaseTest {
80 87
         assertThat(uut).isInstanceOf(ViewController.class);
81 88
     }
82 89
 
90
+    @Test
91
+    public void childrenAreAssignedParent() {
92
+        StackController uut = createStack(Arrays.asList(child1, child2));
93
+        for (ViewController child : uut.getChildControllers()) {
94
+            assertThat(child.getParentController().equals(uut));
95
+        }
96
+    }
97
+
98
+    @Test
99
+    public void createView_currentChildIsAdded() {
100
+        StackController uut = createStack(Arrays.asList(child1, child2, child3, child4));
101
+        assertThat(uut.getChildControllers().size()).isEqualTo(4);
102
+        assertThat(uut.getView().getChildCount()).isEqualTo(2);
103
+        assertThat(uut.getView().getChildAt(1)).isEqualTo(child4.getView());
104
+    }
105
+
83 106
     @Test
84 107
     public void holdsAStackOfViewControllers() {
85 108
         assertThat(uut.isEmpty()).isTrue();
@@ -137,6 +160,7 @@ public class StackControllerTest extends BaseTest {
137 160
 
138 161
     @Test
139 162
     public void animateSetRoot() {
163
+        disablePushAnimation(child1, child2, child3);
140 164
         assertThat(uut.isEmpty()).isTrue();
141 165
         uut.push(child1, new CommandListenerAdapter());
142 166
         uut.push(child2, new CommandListenerAdapter());
@@ -150,7 +174,9 @@ public class StackControllerTest extends BaseTest {
150 174
 
151 175
     @Test
152 176
     public void setRoot() {
153
-        disablePushAnimation(child1, child2);
177
+        activity.setContentView(uut.getView());
178
+        disablePushAnimation(child1, child2, child3);
179
+
154 180
         assertThat(uut.isEmpty()).isTrue();
155 181
         uut.push(child1, new CommandListenerAdapter());
156 182
         uut.push(child2, new CommandListenerAdapter());
@@ -165,7 +191,8 @@ public class StackControllerTest extends BaseTest {
165 191
     }
166 192
 
167 193
     @Test
168
-    public void pop() {
194
+    public synchronized void pop() {
195
+        disablePushAnimation(child1, child2);
169 196
         uut.push(child1, new CommandListenerAdapter());
170 197
         uut.push(child2, new CommandListenerAdapter() {
171 198
             @Override
@@ -191,7 +218,6 @@ public class StackControllerTest extends BaseTest {
191 218
 
192 219
     @Test
193 220
     public void pop_layoutHandlesChildWillDisappear() {
194
-        final StackLayout[] stackLayout = new StackLayout[1];
195 221
         uut = new StackControllerBuilder(activity)
196 222
                         .setTopBarButtonCreator(new TopBarButtonCreatorMock())
197 223
                         .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
@@ -199,7 +225,9 @@ public class StackControllerTest extends BaseTest {
199 225
                         .setTopBarController(new TopBarController())
200 226
                         .setId("uut")
201 227
                         .setInitialOptions(new Options())
228
+                        .setStackPresenter(new StackOptionsPresenter(activity, new Options()))
202 229
                         .build();
230
+        uut.ensureViewIsCreated();
203 231
         uut.push(child1, new CommandListenerAdapter());
204 232
         uut.push(child2, new CommandListenerAdapter() {
205 233
             @Override
@@ -207,7 +235,7 @@ public class StackControllerTest extends BaseTest {
207 235
                 uut.pop(new CommandListenerAdapter() {
208 236
                     @Override
209 237
                     public void onSuccess(String childId) {
210
-                        verify(stackLayout[0], times(1)).onChildWillAppear(child1, child2);
238
+                        verify(presenter, times(1)).onChildWillAppear(child1.options, child2.options);
211 239
                     }
212 240
                 });
213 241
             }
@@ -296,7 +324,8 @@ public class StackControllerTest extends BaseTest {
296 324
         uut.push(child1, new CommandListenerAdapter());
297 325
         assertThat(child1.getParentController()).isEqualTo(uut);
298 326
 
299
-        StackController anotherNavController = createStackController("another");
327
+        StackController anotherNavController = createStack("another");
328
+        anotherNavController.ensureViewIsCreated();
300 329
         anotherNavController.push(child2, new CommandListenerAdapter());
301 330
         assertThat(child2.getParentController()).isEqualTo(anotherNavController);
302 331
     }
@@ -377,6 +406,25 @@ public class StackControllerTest extends BaseTest {
377 406
         });
378 407
     }
379 408
 
409
+    @Test
410
+    public void pop_appearingChildHasCorrectLayoutParams() {
411
+        child2.options.animations.pop.enable = new Bool(false);
412
+        child1.options.topBar.drawBehind = new Bool(false);
413
+
414
+        StackController uut = createStack(Arrays.asList(child1, child2));
415
+        uut.ensureViewIsCreated();
416
+
417
+        assertThat(child2.getView().getParent()).isEqualTo(uut.getView());
418
+        uut.pop(new CommandListenerAdapter());
419
+        assertThat(child1.getView().getParent()).isEqualTo(uut.getView());
420
+
421
+        assertThat(child1.getView().getLayoutParams().width).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT);
422
+        assertThat(child1.getView().getLayoutParams().height).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT);
423
+        assertThat(((ViewGroup.MarginLayoutParams) child1.getView().getLayoutParams()).topMargin).isEqualTo(uut
424
+                .getTopBar()
425
+                .getHeight());
426
+    }
427
+
380 428
     @Test
381 429
     public void popSpecific_deepInStack() {
382 430
         uut.push(child1, new CommandListenerAdapter());
@@ -517,7 +565,8 @@ public class StackControllerTest extends BaseTest {
517 565
 
518 566
     @Test
519 567
     public void findControllerById_Deeply() {
520
-        StackController stack = createStackController("another");
568
+        StackController stack = createStack("another");
569
+        stack.ensureViewIsCreated();
521 570
         stack.push(child2, new CommandListenerAdapter());
522 571
         uut.push(stack, new CommandListenerAdapter());
523 572
         assertThat(uut.findControllerById(child2.getId())).isEqualTo(child2);
@@ -643,7 +692,7 @@ public class StackControllerTest extends BaseTest {
643 692
 
644 693
     @Test
645 694
     public void stackCanBePushed() {
646
-        StackController parent = createStackController("someStack");
695
+        StackController parent = createStack("someStack");
647 696
         parent.ensureViewIsCreated();
648 697
         parent.push(uut, new CommandListenerAdapter());
649 698
         uut.onViewAppeared();
@@ -652,7 +701,7 @@ public class StackControllerTest extends BaseTest {
652 701
 
653 702
     @Test
654 703
     public void applyOptions_applyOnlyOnFirstStack() {
655
-        StackController parent = spy(createStackController("someStack"));
704
+        StackController parent = spy(createStack("someStack"));
656 705
         parent.ensureViewIsCreated();
657 706
         parent.push(uut, new CommandListenerAdapter());
658 707
 
@@ -695,6 +744,7 @@ public class StackControllerTest extends BaseTest {
695 744
                         .setTopBarController(new TopBarController())
696 745
                         .setId("stack")
697 746
                         .setInitialOptions(new Options())
747
+                        .setStackPresenter(new StackOptionsPresenter(activity, new Options()))
698 748
                         .build());
699 749
         Options optionsToMerge = new Options();
700 750
         Component component = mock(Component.class);
@@ -711,9 +761,11 @@ public class StackControllerTest extends BaseTest {
711 761
                         .setTopBarController(new TopBarController())
712 762
                         .setId("stack")
713 763
                         .setInitialOptions(new Options())
764
+                        .setStackPresenter(new StackOptionsPresenter(activity, new Options()))
714 765
                         .build();
715 766
         ParentController parentController = Mockito.mock(ParentController.class);
716 767
         uut.setParentController(parentController);
768
+        uut.ensureViewIsCreated();
717 769
         Options optionsToMerge = new Options();
718 770
         optionsToMerge.topBar.testId = new Text("topBarID");
719 771
         optionsToMerge.bottomTabsOptions.testId = new Text("bottomTabsID");
@@ -738,6 +790,7 @@ public class StackControllerTest extends BaseTest {
738 790
 
739 791
     @Test
740 792
     public void mergeChildOptions_StackRelatedOptionsAreCleared() {
793
+        uut.ensureViewIsCreated();
741 794
         ParentController parentController = Mockito.mock(ParentController.class);
742 795
         uut.setParentController(parentController);
743 796
         Options options = new Options();
@@ -767,17 +820,27 @@ public class StackControllerTest extends BaseTest {
767 820
         assertThat(uut.getChildControllers()).extracting((Extractor<ViewController, String>) ViewController::getId).containsOnly(ids);
768 821
     }
769 822
 
770
-    private StackController createStackController() {
771
-        return createStackController("stack");
823
+    private StackController createStack() {
824
+        return createStack("stack", new ArrayList<>());
825
+    }
826
+
827
+    private StackController createStack(String id) {
828
+        return createStack(id, new ArrayList<>());
829
+    }
830
+
831
+    private StackController createStack(List<ViewController> children) {
832
+        return createStack("stack", children);
772 833
     }
773 834
 
774
-    private StackController createStackController(String id) {
835
+    private StackController createStack(String id, List<ViewController> children) {
775 836
         createTopBarController();
776 837
         return TestUtils.newStackController(activity)
838
+                .setChildren(children)
777 839
                 .setId(id)
778 840
                 .setTopBarController(topBarController)
779 841
                 .setChildRegistry(childRegistry)
780 842
                 .setAnimator(animator)
843
+                .setStackPresenter(presenter)
781 844
                 .build();
782 845
     }
783 846
 
@@ -785,7 +848,9 @@ public class StackControllerTest extends BaseTest {
785 848
         topBarController = spy(new TopBarController() {
786 849
             @Override
787 850
             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()));
851
+                TopBar spy = spy(super.createTopBar(context, buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, stackLayout, ImageLoaderMock.mock()));
852
+                spy.layout(0, 0, 1000, 100);
853
+                return spy;
789 854
             }
790 855
         });
791 856
     }

+ 8
- 3
lib/ios/RNNBottomTabOptions.h Просмотреть файл

@@ -3,14 +3,19 @@
3 3
 @interface RNNBottomTabOptions : RNNOptions
4 4
 
5 5
 @property (nonatomic) NSUInteger tag;
6
-@property (nonatomic, strong) NSString* title;
6
+@property (nonatomic, strong) NSString* text;
7 7
 @property (nonatomic, strong) NSString* badge;
8 8
 @property (nonatomic, strong) NSString* testID;
9 9
 @property (nonatomic, strong) NSNumber* visible;
10 10
 @property (nonatomic, strong) NSDictionary* icon;
11 11
 @property (nonatomic, strong) NSDictionary* selectedIcon;
12
-@property (nonatomic, strong) NSDictionary* disableIconTint;
13
-@property (nonatomic, strong) NSDictionary* disableSelectedIconTint;
12
+@property (nonatomic, strong) NSDictionary* iconColor;
13
+@property (nonatomic, strong) NSDictionary* selectedIconColor;
14
+@property (nonatomic, strong) NSDictionary* textColor;
15
+@property (nonatomic, strong) NSDictionary* selectedTextColor;
16
+@property (nonatomic, strong) NSString* fontFamily;
17
+@property (nonatomic, strong) NSNumber* fontSize;
18
+
14 19
 @property (nonatomic, strong) NSDictionary* iconInsets;
15 20
 
16 21
 @end

+ 83
- 23
lib/ios/RNNBottomTabOptions.m Просмотреть файл

@@ -1,4 +1,5 @@
1 1
 #import "RNNBottomTabOptions.h"
2
+#import "UIImage+tint.h"
2 3
 
3 4
 @implementation RNNBottomTabOptions
4 5
 
@@ -12,14 +13,13 @@
12 13
 }
13 14
 
14 15
 - (void)applyOn:(UIViewController *)viewController {
15
-	if (self.title || self.icon) {
16
-		UIImage *iconImage = nil;
17
-		if (self.disableIconTint) {
18
-			iconImage = [[RCTConvert UIImage:self.icon] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
19
-		} else {
20
-			iconImage = [RCTConvert UIImage:self.icon];
21
-		}
22
-		UITabBarItem* tabItem = [[UITabBarItem alloc] initWithTitle:self.title image:iconImage tag:self.tag];
16
+	if (self.text || self.icon || self.selectedIcon) {
17
+		UITabBarItem* tabItem = viewController.tabBarItem;
18
+		
19
+		tabItem.selectedImage = [self getSelectedIconImage];
20
+		tabItem.image = [self getIconImage];
21
+		tabItem.title = self.text;
22
+		tabItem.tag = self.tag;
23 23
 		tabItem.accessibilityIdentifier = self.testID;
24 24
 		
25 25
 		if (self.iconInsets && ![self.iconInsets isKindOfClass:[NSNull class]]) {
@@ -36,20 +36,9 @@
36 36
 			tabItem.imageInsets = UIEdgeInsetsMake(top, left, bottom, right);
37 37
 		}
38 38
 		
39
-		[viewController.navigationController setTabBarItem:tabItem];
40
-	}
41
-	if (self.selectedIcon) {
42
-		UIImage *selectedIconImage = nil;
43
-		if (self.disableSelectedIconTint) {
44
-			selectedIconImage = [[RCTConvert UIImage:self.selectedIcon] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
45
-		} else {
46
-			selectedIconImage = [RCTConvert UIImage:self.selectedIcon];
47
-		}
48
-		if (viewController.navigationController) {
49
-			viewController.navigationController.tabBarItem.selectedImage = selectedIconImage;
50
-		} else {
51
-			viewController.tabBarItem.selectedImage = selectedIconImage;
52
-		}	
39
+		[self appendTitleAttributes:tabItem];
40
+		
41
+		[viewController setTabBarItem:tabItem];
53 42
 	}
54 43
 	
55 44
 	if (self.badge) {
@@ -71,13 +60,84 @@
71 60
 	[self resetOptions];
72 61
 }
73 62
 
63
+- (UIImage *)getIconImage {
64
+	return [self getIconImageWithTint:self.iconColor];
65
+}
66
+
67
+- (UIImage *)getSelectedIconImage {
68
+	if (self.selectedIcon) {
69
+		if (self.selectedIconColor) {
70
+			return [[[RCTConvert UIImage:self.selectedIcon] withTintColor:[RCTConvert UIColor:self.selectedIconColor]] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
71
+		} else {
72
+			return [[RCTConvert UIImage:self.selectedIcon] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
73
+		}
74
+	} else {
75
+		return [self getIconImageWithTint:self.selectedIconColor];
76
+	}
77
+	
78
+	return nil;
79
+}
80
+
81
+- (UIImage *)getIconImageWithTint:(NSDictionary *)tintColor {
82
+	if (self.icon) {
83
+		if (tintColor) {
84
+			return [[[RCTConvert UIImage:self.icon] withTintColor:[RCTConvert UIColor:tintColor]] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
85
+		} else {
86
+			return [[RCTConvert UIImage:self.icon] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
87
+		}
88
+	}
89
+	
90
+	return nil;
91
+}
92
+
93
+- (void)appendTitleAttributes:(UITabBarItem *)tabItem {
94
+	NSMutableDictionary* selectedAttributes = [NSMutableDictionary dictionaryWithDictionary:[tabItem titleTextAttributesForState:UIControlStateNormal]];
95
+	if (self.selectedTextColor) {
96
+		selectedAttributes[NSForegroundColorAttributeName] = [RCTConvert UIColor:self.selectedTextColor];
97
+	} else {
98
+		selectedAttributes[NSForegroundColorAttributeName] = [UIColor blackColor];
99
+	}
100
+	
101
+	selectedAttributes[NSFontAttributeName] = [self tabBarTextFont];
102
+	[tabItem setTitleTextAttributes:selectedAttributes forState:UIControlStateSelected];
103
+
104
+	
105
+	NSMutableDictionary* normalAttributes = [NSMutableDictionary dictionaryWithDictionary:[tabItem titleTextAttributesForState:UIControlStateNormal]];
106
+	if (self.textColor) {
107
+		normalAttributes[NSForegroundColorAttributeName] = [RCTConvert UIColor:self.textColor];
108
+	} else {
109
+		normalAttributes[NSForegroundColorAttributeName] = [UIColor blackColor];
110
+	}
111
+	
112
+	normalAttributes[NSFontAttributeName] = [self tabBarTextFont];
113
+	[tabItem setTitleTextAttributes:normalAttributes forState:UIControlStateNormal];
114
+}
115
+
116
+
117
+-(UIFont *)tabBarTextFont {
118
+	if (self.fontFamily) {
119
+		return [UIFont fontWithName:self.fontFamily size:self.tabBarTextFontSizeValue];
120
+	}
121
+	else if (self.fontSize) {
122
+		return [UIFont systemFontOfSize:self.tabBarTextFontSizeValue];
123
+	}
124
+	else {
125
+		return nil;
126
+	}
127
+}
128
+
129
+-(CGFloat)tabBarTextFontSizeValue {
130
+	return self.fontSize ? [self.fontSize floatValue] : 10;
131
+}
132
+
74 133
 -(void)resetOptions {
75
-	self.title = nil;
134
+	self.text = nil;
76 135
 	self.badge = nil;
77 136
 	self.visible = nil;
78 137
 	self.icon = nil;
79 138
 	self.testID = nil;
80 139
 	self.iconInsets = nil;
140
+	self.selectedIcon = nil;
81 141
 }
82 142
 
83 143
 @end

+ 2
- 2
lib/ios/RNNBottomTabsOptions.h Просмотреть файл

@@ -9,11 +9,11 @@
9 9
 @property (nonatomic, strong) NSNumber* drawBehind;
10 10
 @property (nonatomic, strong) NSString* currentTabId;
11 11
 
12
+@property (nonatomic, strong) NSNumber* tabColor;
13
+@property (nonatomic, strong) NSNumber* selectedTabColor;
12 14
 @property (nonatomic, strong) NSNumber* translucent;
13 15
 @property (nonatomic, strong) NSNumber* hideShadow;
14 16
 @property (nonatomic, strong) NSNumber* backgroundColor;
15
-@property (nonatomic, strong) NSNumber* tabColor;
16
-@property (nonatomic, strong) NSNumber* selectedTabColor;
17 17
 @property (nonatomic, strong) NSString* fontFamily;
18 18
 @property (nonatomic, strong) NSNumber* fontSize;
19 19
 

+ 1
- 18
lib/ios/RNNBottomTabsOptions.m Просмотреть файл

@@ -48,24 +48,7 @@ extern const NSInteger BLUR_TOPBAR_TAG;
48 48
 	if (self.hideShadow) {
49 49
 		viewController.tabBarController.tabBar.clipsToBounds = [self.hideShadow boolValue];
50 50
 	}
51
-	
52
-	if (self.tabBarTextFont) {
53
-		NSMutableDictionary* tabBarTitleTextAttributes = [NSMutableDictionary new];
54
-		tabBarTitleTextAttributes[NSFontAttributeName] = self.tabBarTextFont;
55
-		
56
-		for (UITabBarItem* item in viewController.tabBarController.tabBar.items) {
57
-			[item setTitleTextAttributes:tabBarTitleTextAttributes forState:UIControlStateNormal];
58
-		}
59
-	}
60
-	
61
-	if (self.tabColor) {
62
-		viewController.tabBarController.tabBar.unselectedItemTintColor = [RCTConvert UIColor:self.tabColor];
63
-	}
64
-	
65
-	if (self.selectedTabColor) {
66
-		viewController.tabBarController.tabBar.tintColor = [RCTConvert UIColor:self.selectedTabColor];
67
-	}
68
-	
51
+
69 52
 	[self resetOptions];
70 53
 }
71 54
 

+ 3
- 2
lib/ios/RNNControllerFactory.m Просмотреть файл

@@ -233,8 +233,9 @@
233 233
 }
234 234
 
235 235
 - (RNNNavigationOptions *)createOptions:(NSDictionary *)optionsDict {
236
-	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:_defaultOptionsDict];
237
-	[options mergeWith:optionsDict];
236
+	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:optionsDict];
237
+	options.defaultOptions = [[RNNNavigationOptions alloc] initWithDict:_defaultOptionsDict];
238
+	
238 239
 	return options;
239 240
 }
240 241
 

+ 2
- 0
lib/ios/RNNNavigationOptions.h Просмотреть файл

@@ -30,6 +30,8 @@ extern const NSInteger TOP_BAR_TRANSPARENT_TAG;
30 30
 @property (nonatomic, strong) RNNPreviewOptions* preview;
31 31
 @property (nonatomic, strong) RNNLayoutOptions* layout;
32 32
 
33
+@property (nonatomic, strong) RNNOptions* defaultOptions;
34
+
33 35
 @property (nonatomic, strong) NSMutableDictionary* originalTopBarImages;
34 36
 @property (nonatomic, strong) NSNumber* popGesture;
35 37
 @property (nonatomic, strong) NSDictionary* backgroundImage;

+ 1
- 0
lib/ios/RNNNavigationOptions.m Просмотреть файл

@@ -43,6 +43,7 @@ RCT_ENUM_CONVERTER(UIModalTransitionStyle,
43 43
 
44 44
 
45 45
 -(void)applyOn:(UIViewController<RNNRootViewProtocol> *)viewController {
46
+	[self mergeOptions:_defaultOptions overrideOptions:NO];
46 47
 	[self.topBar applyOn:viewController];
47 48
 	[self.bottomTabs applyOn:viewController];
48 49
 	[self.topTab applyOn:viewController];

+ 1
- 0
lib/ios/RNNUtils.h Просмотреть файл

@@ -1,4 +1,5 @@
1 1
 #import <Foundation/Foundation.h>
2
+#import <UIKit/UIKit.h>
2 3
 
3 4
 @interface RNNUtils : NSObject
4 5
 

+ 8
- 0
lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj Просмотреть файл

@@ -93,6 +93,8 @@
93 93
 		5064495E20DC62B90026709C /* RNNSideMenuSideOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 5064495C20DC62B90026709C /* RNNSideMenuSideOptions.m */; };
94 94
 		506A2B1420973DFD00F43A95 /* RNNErrorHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 506A2B1220973DFD00F43A95 /* RNNErrorHandler.h */; };
95 95
 		506A2B1520973DFD00F43A95 /* RNNErrorHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 506A2B1320973DFD00F43A95 /* RNNErrorHandler.m */; };
96
+		50706E6D20CE7CA5003345C3 /* UIImage+tint.h in Headers */ = {isa = PBXBuildFile; fileRef = 50706E6B20CE7CA5003345C3 /* UIImage+tint.h */; };
97
+		50706E6E20CE7CA5003345C3 /* UIImage+tint.m in Sources */ = {isa = PBXBuildFile; fileRef = 50706E6C20CE7CA5003345C3 /* UIImage+tint.m */; };
96 98
 		50762D08205E96C200E3D18A /* RNNModalAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 50762D06205E96C200E3D18A /* RNNModalAnimation.h */; };
97 99
 		50762D09205E96C200E3D18A /* RNNModalAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 50762D07205E96C200E3D18A /* RNNModalAnimation.m */; };
98 100
 		507E7D57201DDD3000444E6C /* RNNAnimationOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 507E7D55201DDD3000444E6C /* RNNAnimationOptions.h */; };
@@ -307,6 +309,8 @@
307 309
 		5064495C20DC62B90026709C /* RNNSideMenuSideOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNSideMenuSideOptions.m; sourceTree = "<group>"; };
308 310
 		506A2B1220973DFD00F43A95 /* RNNErrorHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNErrorHandler.h; sourceTree = "<group>"; };
309 311
 		506A2B1320973DFD00F43A95 /* RNNErrorHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNErrorHandler.m; sourceTree = "<group>"; };
312
+		50706E6B20CE7CA5003345C3 /* UIImage+tint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIImage+tint.h"; sourceTree = "<group>"; };
313
+		50706E6C20CE7CA5003345C3 /* UIImage+tint.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIImage+tint.m"; sourceTree = "<group>"; };
310 314
 		50762D06205E96C200E3D18A /* RNNModalAnimation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNModalAnimation.h; sourceTree = "<group>"; };
311 315
 		50762D07205E96C200E3D18A /* RNNModalAnimation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNModalAnimation.m; sourceTree = "<group>"; };
312 316
 		507E7D55201DDD3000444E6C /* RNNAnimationOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNAnimationOptions.h; sourceTree = "<group>"; };
@@ -462,6 +466,8 @@
462 466
 				390AD476200F499D00A8250D /* RNNSwizzles.m */,
463 467
 				506A2B1220973DFD00F43A95 /* RNNErrorHandler.h */,
464 468
 				506A2B1320973DFD00F43A95 /* RNNErrorHandler.m */,
469
+				50706E6B20CE7CA5003345C3 /* UIImage+tint.h */,
470
+				50706E6C20CE7CA5003345C3 /* UIImage+tint.m */,
465 471
 			);
466 472
 			name = Helpers;
467 473
 			sourceTree = "<group>";
@@ -869,6 +875,7 @@
869 875
 				50451D0D2042F70900695F00 /* RNNTransition.h in Headers */,
870 876
 				507F43F81FF525B500D9425B /* RNNSegmentedControl.h in Headers */,
871 877
 				50175CD1207A2AA1004FE91B /* RNNComponentOptions.h in Headers */,
878
+				50706E6D20CE7CA5003345C3 /* UIImage+tint.h in Headers */,
872 879
 			);
873 880
 			runOnlyForDeploymentPostprocessing = 0;
874 881
 		};
@@ -1040,6 +1047,7 @@
1040 1047
 				507F43F51FF4FCFE00D9425B /* HMSegmentedControl.m in Sources */,
1041 1048
 				50451D0A2042E20600695F00 /* RNNTransitionsOptions.m in Sources */,
1042 1049
 				507F43C61FF4F17C00D9425B /* RNNTopTabsViewController.m in Sources */,
1050
+				50706E6E20CE7CA5003345C3 /* UIImage+tint.m in Sources */,
1043 1051
 				50A00C38200F84D6000F01A6 /* RNNOverlayOptions.m in Sources */,
1044 1052
 				50762D09205E96C200E3D18A /* RNNModalAnimation.m in Sources */,
1045 1053
 				50EB93421FE14A3E00BD8EEE /* RNNBottomTabOptions.m in Sources */,

+ 12
- 23
lib/ios/ReactNativeNavigationTests/RNNRootViewControllerTest.m Просмотреть файл

@@ -671,25 +671,10 @@
671 671
 	XCTAssertTrue([self.uut.tabBarController.tabBar.barTintColor isEqual:expectedColor]);
672 672
 }
673 673
 
674
--(void)testTabBarSelectedColor_validColor{
675
-	NSNumber* inputColor = @(0xFFFF0000);
676
-	self.options.bottomTabs.tabColor = inputColor;
677
-	[self.uut embedInTabBarController];
678
-	UIColor* expectedColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:1];
679
-	XCTAssertTrue([self.uut.tabBarController.tabBar.unselectedItemTintColor isEqual:expectedColor]);
680
-}
681
-
682
--(void)testTabBarUnselectedColor_validColor{
683
-	NSNumber* inputColor = @(0xFFFF0000);
684
-	self.options.bottomTabs.selectedTabColor = inputColor;
685
-	[self.uut embedInTabBarController];
686
-	UIColor* expectedColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:1];
687
-	XCTAssertTrue([self.uut.tabBarController.tabBar.tintColor isEqual:expectedColor]);
688
-}
689
-
690 674
 -(void)testTabBarTextFontFamily_validFont{
691 675
 	NSString* inputFont = @"HelveticaNeue";
692
-	self.options.bottomTabs.fontFamily = inputFont;
676
+	self.options.bottomTab.fontFamily = inputFont;
677
+	self.options.bottomTab.text = @"Tab 1";
693 678
 	[self.uut embedInTabBarController];
694 679
 	UIFont* expectedFont = [UIFont fontWithName:inputFont size:10];
695 680
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];
@@ -697,7 +682,8 @@
697 682
 }
698 683
 
699 684
 -(void)testTabBarTextFontSize_withoutTextFontFamily_withoutTextColor {
700
-	self.options.bottomTabs.fontSize = @(15);
685
+	self.options.bottomTab.fontSize = @(15);
686
+	self.options.bottomTab.text = @"Tab 1";
701 687
 	[self.uut embedInTabBarController];
702 688
 	UIFont* expectedFont = [UIFont systemFontOfSize:15];
703 689
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];
@@ -705,7 +691,8 @@
705 691
 }
706 692
 
707 693
 -(void)testTabBarTextFontSize_withoutTextFontFamily {
708
-	self.options.bottomTabs.fontSize = @(15);
694
+	self.options.bottomTab.fontSize = @(15);
695
+	self.options.bottomTab.text = @"Tab 1";
709 696
 	[self.uut embedInTabBarController];
710 697
 	UIFont* expectedFont = [UIFont systemFontOfSize:15];
711 698
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];
@@ -714,8 +701,9 @@
714 701
 
715 702
 -(void)testTabBarTextFontSize_withTextFontFamily_withTextColor {
716 703
 	NSString* inputFont = @"HelveticaNeue";
717
-	self.options.bottomTabs.fontSize = @(15);
718
-	self.options.bottomTabs.fontFamily = inputFont;
704
+	self.options.bottomTab.text = @"Tab 1";
705
+	self.options.bottomTab.fontSize = @(15);
706
+	self.options.bottomTab.fontFamily = inputFont;
719 707
 	[self.uut embedInTabBarController];
720 708
 	UIFont* expectedFont = [UIFont fontWithName:inputFont size:15];
721 709
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];
@@ -724,8 +712,9 @@
724 712
 
725 713
 -(void)testTabBarTextFontSize_withTextFontFamily_withoutTextColor {
726 714
 	NSString* inputFont = @"HelveticaNeue";
727
-	self.options.bottomTabs.fontSize = @(15);
728
-	self.options.bottomTabs.fontFamily = inputFont;
715
+	self.options.bottomTab.text = @"Tab 1";
716
+	self.options.bottomTab.fontSize = @(15);
717
+	self.options.bottomTab.fontFamily = inputFont;
729 718
 	[self.uut embedInTabBarController];
730 719
 	UIFont* expectedFont = [UIFont fontWithName:inputFont size:15];
731 720
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];

+ 7
- 0
lib/ios/UIImage+tint.h Просмотреть файл

@@ -0,0 +1,7 @@
1
+#import <UIKit/UIKit.h>
2
+
3
+@interface UIImage (tint)
4
+
5
+- (UIImage *)withTintColor:(UIColor *)color;
6
+
7
+@end

+ 29
- 0
lib/ios/UIImage+tint.m Просмотреть файл

@@ -0,0 +1,29 @@
1
+#import "UIImage+tint.h"
2
+
3
+@implementation UIImage (tint)
4
+
5
+- (UIImage *)withTintColor:(UIColor *)color {
6
+	UIGraphicsBeginImageContext(self.size);
7
+	CGContextRef context = UIGraphicsGetCurrentContext();
8
+	
9
+	CGContextTranslateCTM(context, 0, self.size.height);
10
+	CGContextScaleCTM(context, 1.0, -1.0);
11
+	
12
+	CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
13
+	
14
+	// draw alpha-mask
15
+	CGContextSetBlendMode(context, kCGBlendModeNormal);
16
+	CGContextDrawImage(context, rect, self.CGImage);
17
+	
18
+	// draw tint color, preserving alpha values of original image
19
+	CGContextSetBlendMode(context, kCGBlendModeSourceIn);
20
+	[color setFill];
21
+	CGContextFillRect(context, rect);
22
+	
23
+	UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
24
+	UIGraphicsEndImageContext();
25
+	
26
+	return coloredImage;
27
+}
28
+
29
+@end

+ 1
- 0
playground/android/build.gradle Просмотреть файл

@@ -25,5 +25,6 @@ allprojects {
25 25
             // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
26 26
             url "$rootDir/../../node_modules/react-native/android"
27 27
         }
28
+        maven { url 'https://jitpack.io' }
28 29
     }
29 30
 }

+ 8
- 0
playground/src/app.js Просмотреть файл

@@ -24,6 +24,14 @@ function start() {
24 24
   registerScreens();
25 25
   Navigation.events().registerAppLaunchedListener(() => {
26 26
     Navigation.setDefaultOptions({
27
+      bottomTab: {
28
+        iconColor: '#1B4C77',
29
+        selectedIconColor: '#0f0',
30
+        textColor: '#1B4C77',
31
+        selectedTextColor: '#0f0',
32
+        fontFamily: 'HelveticaNeue-Italic',
33
+        fontSize: 13
34
+      },
27 35
       _animations: {
28 36
         startApp: {
29 37
           y: {

+ 3
- 5
playground/src/screens/TextScreen.js Просмотреть файл

@@ -25,7 +25,7 @@ class TextScreen extends Component {
25 25
         <Text style={styles.h1} testID={testIDs.CENTERED_TEXT_HEADER}>{this.props.text || 'Text Screen'}</Text>
26 26
         {this.renderTextFromFunctionInProps()}
27 27
         <Text style={styles.footer}>{`this.props.componentId = ${this.props.componentId}`}</Text>
28
-        <Button title={'Set Tab Badge'} testID={testIDs.SET_TAB_BADGE_BUTTON} onPress={() => this.onButtonPress()} />
28
+        <Button title={'Set Tab Badge'} testID={testIDs.SET_TAB_BADGE_BUTTON} onPress={() => this.onClickSetBadge()} />
29 29
         <Button title={'Switch To Tab 2'} testID={testIDs.SWITCH_SECOND_TAB_BUTTON} onPress={() => this.onClickSwitchToTab()} />
30 30
         <Button title={'Switch To Tab 1 by componentID'} testID={testIDs.SWITCH_FIRST_TAB_BUTTON} onPress={() => this.onClickSwitchToTabByComponentID()} />
31 31
         <Button title='Hide Tab Bar' testID={testIDs.HIDE_BOTTOM_TABS_BUTTON} onPress={() => this.hideTabBar(false)} />
@@ -59,7 +59,7 @@ class TextScreen extends Component {
59 59
     );
60 60
   }
61 61
 
62
-  onButtonPress() {
62
+  onClickSetBadge() {
63 63
     Navigation.mergeOptions(this.props.componentId, {
64 64
       bottomTab: {
65 65
         badge: `TeSt`
@@ -73,9 +73,7 @@ class TextScreen extends Component {
73 73
         currentTabIndex: 1,
74 74
         visible: false,
75 75
         drawBehind: true,
76
-        animate: true,
77
-        tabColor: 'blue',
78
-        selectedTabColor: 'red'
76
+        animate: true
79 77
       }
80 78
     });
81 79
   }

+ 24
- 13
playground/src/screens/WelcomeScreen.js Просмотреть файл

@@ -92,8 +92,9 @@ class WelcomeScreen extends Component {
92 92
                 ],
93 93
                 options: {
94 94
                   bottomTab: {
95
-                    title: 'Tab 1',
95
+                    text: 'Tab 1',
96 96
                     icon: require('../images/one.png'),
97
+                    selectedIcon: require('../images/one.png'),
97 98
                     testID: testIDs.FIRST_TAB_BAR_BUTTON
98 99
                   },
99 100
                   topBar: {
@@ -116,7 +117,7 @@ class WelcomeScreen extends Component {
116 117
                 ],
117 118
                 options: {
118 119
                   bottomTab: {
119
-                    title: 'Tab 2',
120
+                    text: 'Tab 2',
120 121
                     icon: require('../images/two.png'),
121 122
                     testID: testIDs.SECOND_TAB_BAR_BUTTON
122 123
                   }
@@ -126,11 +127,7 @@ class WelcomeScreen extends Component {
126 127
           ],
127 128
           options: {
128 129
             bottomTabs: {
129
-              tabColor: 'red',
130 130
               titleDisplayMode: 'alwaysShow',
131
-              selectedTabColor: 'blue',
132
-              fontFamily: 'HelveticaNeue-Italic',
133
-              fontSize: 13,
134 131
               testID: testIDs.BOTTOM_TABS_ELEMENT
135 132
             }
136 133
           }
@@ -163,13 +160,25 @@ class WelcomeScreen extends Component {
163 160
                           name: 'navigation.playground.TextScreen',
164 161
                           passProps: {
165 162
                             text: 'This is a side menu center screen tab 1'
166
-                          }
163
+                          },
164
+                          // options: {
165
+                          //   bottomTab: {
166
+                          //     iconColor: 'red',
167
+                          //     textColor: 'red',
168
+                          //     selectedIconColor: 'purple',
169
+                          //     selectedTextColor: 'purple',
170
+                          //   }
171
+                          // }
167 172
                         }
168 173
                       }
169 174
                     ],
170 175
                     options: {
171 176
                       bottomTab: {
172
-                        title: 'Tab 1',
177
+                        iconColor: 'red',
178
+                        textColor: 'red',
179
+                        selectedIconColor: 'purple',
180
+                        selectedTextColor: 'purple',
181
+                        text: 'Tab 1',
173 182
                         icon: require('../images/one.png'),
174 183
                         testID: testIDs.FIRST_TAB_BAR_BUTTON
175 184
                       }
@@ -190,7 +199,7 @@ class WelcomeScreen extends Component {
190 199
                     ],
191 200
                     options: {
192 201
                       bottomTab: {
193
-                        title: 'Tab 2',
202
+                        text: 'Tab 2',
194 203
                         icon: require('../images/two.png'),
195 204
                         testID: testIDs.SECOND_TAB_BAR_BUTTON
196 205
                       }
@@ -211,7 +220,7 @@ class WelcomeScreen extends Component {
211 220
                     ],
212 221
                     options: {
213 222
                       bottomTab: {
214
-                        title: 'Tab 3',
223
+                        text: 'Tab 3',
215 224
                         icon: require('../images/three.png'),
216 225
                         testID: testIDs.SECOND_TAB_BAR_BUTTON
217 226
                       }
@@ -220,9 +229,11 @@ class WelcomeScreen extends Component {
220 229
                 }
221 230
               ],
222 231
               options: {
223
-                bottomTabs: {
224
-                  tabColor: 'red',
225
-                  selectedTabColor: 'blue',
232
+                bottomTab: {
233
+                  textColor: '#AED581',
234
+                  iconColor: '#AED581',
235
+                  selectedTextColor: '#90CAF9',
236
+                  selectedIconColor: '#90CAF9',
226 237
                   fontFamily: 'HelveticaNeue-Italic',
227 238
                   fontSize: 13
228 239
                 }