Browse Source

[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 years ago
parent
commit
2c33830f3e
63 changed files with 1090 additions and 395 deletions
  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 View File

80
 	           // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
80
 	           // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
81
 	           url "$rootDir/../node_modules/react-native/android"
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 View File

120
     currentTabId: 'currentTabId',
120
     currentTabId: 'currentTabId',
121
     testID: 'bottomTabsTestID',
121
     testID: 'bottomTabsTestID',
122
     drawBehind: false,
122
     drawBehind: false,
123
-    backgroundColor: 'white',
124
-    tabColor: 'red',
125
-    selectedTabColor: 'blue',
126
-    fontFamily: 'Helvetica',
127
-    fontSize: 10
123
+    backgroundColor: 'white'
128
   },
124
   },
129
   bottomTab: {
125
   bottomTab: {
130
     title: 'Tab 1',
126
     title: 'Tab 1',
131
     badge: '2',
127
     badge: '2',
132
     testID: 'bottomTabTestID',
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
   sideMenu: {
137
   sideMenu: {
136
     left: {
138
     left: {

+ 5
- 1
lib/android/app/build.gradle View File

62
         p.android.compileOptions.sourceCompatibility JavaVersion.VERSION_1_8
62
         p.android.compileOptions.sourceCompatibility JavaVersion.VERSION_1_8
63
         p.android.compileOptions.targetCompatibility JavaVersion.VERSION_1_8
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
 dependencies {
70
 dependencies {
69
     implementation 'com.android.support:design:26.1.0'
72
     implementation 'com.android.support:design:26.1.0'
70
     implementation 'com.android.support:appcompat-v7:26.1.0'
73
     implementation 'com.android.support:appcompat-v7:26.1.0'
71
     implementation 'com.android.support:support-v4:26.1.0'
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
     implementation 'com.github.clans:fab:1.6.4'
77
     implementation 'com.github.clans:fab:1.6.4'
74
 
78
 
75
     //noinspection GradleDynamicVersion
79
     //noinspection GradleDynamicVersion

+ 39
- 27
lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabOptions.java View File

1
 package com.reactnativenavigation.parse;
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
 import com.reactnativenavigation.parse.params.NullText;
8
 import com.reactnativenavigation.parse.params.NullText;
4
 import com.reactnativenavigation.parse.params.Text;
9
 import com.reactnativenavigation.parse.params.Text;
10
+import com.reactnativenavigation.parse.parsers.ColorParser;
5
 import com.reactnativenavigation.parse.parsers.TextParser;
11
 import com.reactnativenavigation.parse.parsers.TextParser;
12
+import com.reactnativenavigation.utils.TypefaceLoader;
6
 
13
 
7
 import org.json.JSONObject;
14
 import org.json.JSONObject;
8
 
15
 
9
 public class BottomTabOptions {
16
 public class BottomTabOptions {
10
 
17
 
11
-    public static BottomTabOptions parse(JSONObject json) {
18
+    public static BottomTabOptions parse(TypefaceLoader typefaceManager, JSONObject json) {
12
         BottomTabOptions options = new BottomTabOptions();
19
         BottomTabOptions options = new BottomTabOptions();
13
         if (json == null) return options;
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
         options.badge = TextParser.parse(json, "badge");
28
         options.badge = TextParser.parse(json, "badge");
20
         options.testId = TextParser.parse(json, "testID");
29
         options.testId = TextParser.parse(json, "testID");
30
+        options.fontFamily = typefaceManager.getTypeFace(json.optString("fontFamily", ""));
21
         return options;
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
     public Text icon = new NullText();
37
     public Text icon = new NullText();
38
+    public Color iconColor = new NullColor();
39
+    public Color selectedIconColor = new NullColor();
26
     public Text testId = new NullText();
40
     public Text testId = new NullText();
27
     public Text badge = new NullText();
41
     public Text badge = new NullText();
42
+    @Nullable public Typeface fontFamily;
43
+
28
 
44
 
29
     void mergeWith(final BottomTabOptions other) {
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
     void mergeWithDefault(final BottomTabOptions defaultOptions) {
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 View File

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

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

3
 import android.app.Activity;
3
 import android.app.Activity;
4
 
4
 
5
 import com.facebook.react.ReactInstanceManager;
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
 import com.reactnativenavigation.react.EventEmitter;
10
 import com.reactnativenavigation.react.EventEmitter;
7
-import com.reactnativenavigation.utils.CommandListenerAdapter;
8
 import com.reactnativenavigation.utils.ImageLoader;
11
 import com.reactnativenavigation.utils.ImageLoader;
9
 import com.reactnativenavigation.utils.TypefaceLoader;
12
 import com.reactnativenavigation.utils.TypefaceLoader;
10
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
13
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
11
 import com.reactnativenavigation.viewcontrollers.ComponentViewController;
14
 import com.reactnativenavigation.viewcontrollers.ComponentViewController;
12
 import com.reactnativenavigation.viewcontrollers.SideMenuController;
15
 import com.reactnativenavigation.viewcontrollers.SideMenuController;
13
-import com.reactnativenavigation.viewcontrollers.stack.StackController;
14
-import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
15
 import com.reactnativenavigation.viewcontrollers.ViewController;
16
 import com.reactnativenavigation.viewcontrollers.ViewController;
16
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabsController;
17
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabsController;
17
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentCreator;
18
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentCreator;
18
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentViewController;
19
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentViewController;
20
+import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
19
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
21
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
20
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
22
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
21
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
23
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
29
 import java.util.List;
31
 import java.util.List;
30
 import java.util.Map;
32
 import java.util.Map;
31
 
33
 
34
+import static com.reactnativenavigation.parse.Options.parse;
35
+
32
 public class LayoutFactory {
36
 public class LayoutFactory {
33
 
37
 
34
 	private final Activity activity;
38
 	private final Activity activity;
75
 	}
79
 	}
76
 
80
 
77
     private ViewController createSideMenuRoot(LayoutNode node) {
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
 		ViewController childControllerCenter = null, childControllerLeft = null, childControllerRight = null;
88
 		ViewController childControllerCenter = null, childControllerLeft = null, childControllerRight = null;
80
 
89
 
81
 		for (LayoutNode child : node.children) {
90
 		for (LayoutNode child : node.children) {
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
 		if (childControllerCenter != null) {
109
 		if (childControllerCenter != null) {
106
 			sideMenuController.setCenterController(childControllerCenter);
110
 			sideMenuController.setCenterController(childControllerCenter);
107
 		}
111
 		}
137
                 id,
141
                 id,
138
                 name,
142
                 name,
139
                 new ComponentViewCreator(reactInstanceManager),
143
                 new ComponentViewCreator(reactInstanceManager),
140
-                parseNodeOptions(node)
144
+                parse(typefaceManager, node.getOptions()),
145
+                new OptionsPresenter(activity, defaultOptions)
141
         );
146
         );
142
 	}
147
 	}
143
 
148
 
148
                 externalComponent,
153
                 externalComponent,
149
                 externalComponentCreators.get(externalComponent.name.get()),
154
                 externalComponentCreators.get(externalComponent.name.get()),
150
                 reactInstanceManager,
155
                 reactInstanceManager,
151
-                parseNodeOptions(node)
156
+                parse(typefaceManager, node.getOptions())
152
         );
157
         );
153
     }
158
     }
154
 
159
 
155
 	private ViewController createStack(LayoutNode node) {
160
 	private ViewController createStack(LayoutNode node) {
156
-        StackController stackController = new StackControllerBuilder(activity)
161
+        return new StackControllerBuilder(activity)
162
+                .setChildren(createChildredn(node.children))
157
                 .setChildRegistry(childRegistry)
163
                 .setChildRegistry(childRegistry)
158
                 .setTopBarButtonCreator(new TitleBarButtonCreator(reactInstanceManager))
164
                 .setTopBarButtonCreator(new TitleBarButtonCreator(reactInstanceManager))
159
                 .setTitleBarReactViewCreator(new TitleBarReactViewCreator(reactInstanceManager))
165
                 .setTitleBarReactViewCreator(new TitleBarReactViewCreator(reactInstanceManager))
160
                 .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreator(reactInstanceManager)))
166
                 .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreator(reactInstanceManager)))
161
                 .setTopBarController(new TopBarController())
167
                 .setTopBarController(new TopBarController())
162
                 .setId(node.id)
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
                 .build();
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
         for (LayoutNode child : children) {
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
     private ViewController createBottomTabs(LayoutNode node) {
183
     private ViewController createBottomTabs(LayoutNode node) {
177
         for (int i = 0; i < node.children.size(); i++) {
185
         for (int i = 0; i < node.children.size(); i++) {
178
             tabs.add(create(node.children.get(i)));
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
     private ViewController createTopTabs(LayoutNode node) {
200
     private ViewController createTopTabs(LayoutNode node) {
184
         final List<ViewController> tabs = new ArrayList<>();
201
         final List<ViewController> tabs = new ArrayList<>();
185
         for (int i = 0; i < node.children.size(); i++) {
202
         for (int i = 0; i < node.children.size(); i++) {
186
             ViewController tabController = create(node.children.get(i));
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
             options.setTopTabIndex(i);
205
             options.setTopTabIndex(i);
189
             tabs.add(tabController);
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 View File

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

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

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

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

1
 package com.reactnativenavigation.parse;
1
 package com.reactnativenavigation.parse;
2
 
2
 
3
+import android.support.annotation.CheckResult;
4
+
3
 import com.reactnativenavigation.parse.params.Orientation;
5
 import com.reactnativenavigation.parse.params.Orientation;
4
 
6
 
5
 import org.json.JSONArray;
7
 import org.json.JSONArray;
52
         return !orientations.isEmpty();
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
     @Override
69
     @Override
56
     public String toString() {
70
     public String toString() {
57
         return hasValue() ? Arrays.toString(orientations.toArray(new Orientation[0])) : Orientation.Default.toString();
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 View File

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 View File

1
 package com.reactnativenavigation.presentation;
1
 package com.reactnativenavigation.presentation;
2
 
2
 
3
+import android.view.ViewGroup;
3
 import android.view.ViewGroup.MarginLayoutParams;
4
 import android.view.ViewGroup.MarginLayoutParams;
4
 
5
 
5
 import com.reactnativenavigation.anim.BottomTabsAnimator;
6
 import com.reactnativenavigation.anim.BottomTabsAnimator;
6
 import com.reactnativenavigation.parse.AnimationsOptions;
7
 import com.reactnativenavigation.parse.AnimationsOptions;
7
-import com.reactnativenavigation.parse.BottomTabOptions;
8
 import com.reactnativenavigation.parse.BottomTabsOptions;
8
 import com.reactnativenavigation.parse.BottomTabsOptions;
9
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.parse.Options;
10
+import com.reactnativenavigation.utils.UiUtils;
10
 import com.reactnativenavigation.viewcontrollers.ViewController;
11
 import com.reactnativenavigation.viewcontrollers.ViewController;
11
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabFinder;
12
 import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabFinder;
12
 import com.reactnativenavigation.viewcontrollers.bottomtabs.TabSelector;
13
 import com.reactnativenavigation.viewcontrollers.bottomtabs.TabSelector;
16
 import java.util.List;
17
 import java.util.List;
17
 
18
 
18
 public class BottomTabsOptionsPresenter {
19
 public class BottomTabsOptionsPresenter {
19
-    private final BottomTabs bottomTabs;
20
-    private final TabSelector tabSelector;
21
     private final BottomTabFinder bottomTabFinder;
20
     private final BottomTabFinder bottomTabFinder;
22
-    private final BottomTabsAnimator animator;
23
     private final List<ViewController> tabs;
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
         this.tabs = tabs;
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
         this.tabSelector = tabSelector;
39
         this.tabSelector = tabSelector;
29
-        this.bottomTabFinder = bottomTabFinder;
30
         animator = new BottomTabsAnimator(bottomTabs);
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
     public void present(Options options) {
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
     public void presentChildOptions(Options options, Component child) {
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
         int tabIndex = bottomTabFinder.findByComponent(child);
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
     private void applyDrawBehind(BottomTabsOptions options, int tabIndex) {
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
         if (options.drawBehind.isTrue()) {
63
         if (options.drawBehind.isTrue()) {
53
             lp.bottomMargin = 0;
64
             lp.bottomMargin = 0;
54
         }
65
         }
55
         if (options.visible.isTrueOrUndefined() && options.drawBehind.isFalseOrUndefined()) {
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
         if (options.testId.hasValue()) {
86
         if (options.testId.hasValue()) {
72
             bottomTabs.setTag(options.testId.get());
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
         if (options.currentTabId.hasValue()) {
89
         if (options.currentTabId.hasValue()) {
81
             int tabIndex = bottomTabFinder.findByControllerId(options.currentTabId.get());
90
             int tabIndex = bottomTabFinder.findByControllerId(options.currentTabId.get());
82
             if (tabIndex >= 0) tabSelector.selectTab(tabIndex);
91
             if (tabIndex >= 0) tabSelector.selectTab(tabIndex);

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

4
 import android.graphics.Color;
4
 import android.graphics.Color;
5
 import android.os.Build;
5
 import android.os.Build;
6
 import android.view.View;
6
 import android.view.View;
7
+import android.view.ViewGroup;
7
 import android.view.ViewGroup.MarginLayoutParams;
8
 import android.view.ViewGroup.MarginLayoutParams;
8
 
9
 
9
 import com.reactnativenavigation.parse.Options;
10
 import com.reactnativenavigation.parse.Options;
17
 public class OptionsPresenter {
18
 public class OptionsPresenter {
18
 
19
 
19
     private Activity activity;
20
     private Activity activity;
21
+    private Options defaultOptions;
20
 
22
 
21
-    public OptionsPresenter(Activity activity) {
23
+    public OptionsPresenter(Activity activity, Options defaultOptions) {
22
         this.activity = activity;
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
     public void present(View view, Options options) {
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
     public void applyRootOptions(View view, Options options) {
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
     private void applyOrientation(OrientationOptions options) {
53
     private void applyOrientation(OrientationOptions options) {
45
         }
63
         }
46
     }
64
     }
47
 
65
 
48
-    public void onViewBroughtToFront(View view, Options options) {
49
-        applyStatusBarOptions(view, options.statusBar);
50
-    }
51
-
52
     private void applyStatusBarOptions(View view, StatusBarOptions statusBar) {
66
     private void applyStatusBarOptions(View view, StatusBarOptions statusBar) {
53
         setStatusBarBackgroundColor(statusBar);
67
         setStatusBarBackgroundColor(statusBar);
54
         setTextColorScheme(statusBar.textColorScheme);
68
         setTextColorScheme(statusBar.textColorScheme);

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

1
 package com.reactnativenavigation.presentation;
1
 package com.reactnativenavigation.presentation;
2
 
2
 
3
 import android.app.Activity;
3
 import android.app.Activity;
4
+import android.content.Context;
4
 import android.graphics.Color;
5
 import android.graphics.Color;
6
+import android.view.View;
5
 import android.view.ViewGroup.LayoutParams;
7
 import android.view.ViewGroup.LayoutParams;
6
 
8
 
7
 import com.reactnativenavigation.parse.AnimationsOptions;
9
 import com.reactnativenavigation.parse.AnimationsOptions;
24
     private final double defaultSubtitleFontSize;
26
     private final double defaultSubtitleFontSize;
25
 
27
 
26
     private TopBar topBar;
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
         this.topBar = topBar;
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
     public void applyChildOptions(Options options, Component child) {
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
     public void applyOrientation(OrientationOptions options) {
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
     private void applyTopBarOptions(TopBarOptions options, AnimationsOptions animationOptions, Component component, Options componentOptions) {
70
     private void applyTopBarOptions(TopBarOptions options, AnimationsOptions animationOptions, Component component, Options componentOptions) {
124
     }
148
     }
125
 
149
 
126
     public void mergeChildOptions(Options options, Component child) {
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
     private void mergeOrientation(OrientationOptions orientationOptions) {
159
     private void mergeOrientation(OrientationOptions orientationOptions) {

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

76
     }
76
     }
77
 
77
 
78
     public static int getPreferredHeight(View view) {
78
     public static int getPreferredHeight(View view) {
79
+        if (view.getLayoutParams() == null) return 0;
79
         return view.getLayoutParams().height < 0 ? view.getHeight() : view.getLayoutParams().height;
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 View File

20
         this.childRegistry = childRegistry;
20
         this.childRegistry = childRegistry;
21
     }
21
     }
22
 
22
 
23
+    @Override
24
+    public void setDefaultOptions(Options defaultOptions) {
25
+        presenter.setDefaultOptions(defaultOptions);
26
+    }
27
+
23
     @Override
28
     @Override
24
     public void onViewAppeared() {
29
     public void onViewAppeared() {
25
         super.onViewAppeared();
30
         super.onViewAppeared();

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

19
                                    final String id,
19
                                    final String id,
20
                                    final String componentName,
20
                                    final String componentName,
21
                                    final ReactViewCreator viewCreator,
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
         this.componentName = componentName;
25
         this.componentName = componentName;
25
         this.viewCreator = viewCreator;
26
         this.viewCreator = viewCreator;
26
     }
27
     }

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

33
     private final OverlayManager overlayManager;
33
     private final OverlayManager overlayManager;
34
     private Options defaultOptions = new Options();
34
     private Options defaultOptions = new Options();
35
 
35
 
36
+    @Override
36
     public void setDefaultOptions(Options defaultOptions) {
37
     public void setDefaultOptions(Options defaultOptions) {
37
         this.defaultOptions = defaultOptions;
38
         this.defaultOptions = defaultOptions;
39
+        if (root != null) root.setDefaultOptions(defaultOptions);
38
     }
40
     }
39
 
41
 
40
     public Options getDefaultOptions() {
42
     public Options getDefaultOptions() {
42
     }
44
     }
43
 
45
 
44
     public Navigator(final Activity activity, ChildControllersRegistry childRegistry, OverlayManager overlayManager) {
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
         modalStack = new ModalStack(new ModalPresenter(new ModalAnimator(activity)));
48
         modalStack = new ModalStack(new ModalPresenter(new ModalAnimator(activity)));
47
         this.overlayManager = overlayManager;
49
         this.overlayManager = overlayManager;
48
     }
50
     }
73
         return modalStack.handleBack(listener, root);
75
         return modalStack.handleBack(listener, root);
74
     }
76
     }
75
 
77
 
78
+    @Override
79
+    protected ViewController getCurrentChild() {
80
+        return root;
81
+    }
82
+
76
     @Override
83
     @Override
77
     public void onReload() {
84
     public void onReload() {
78
         destroyViews();
85
         destroyViews();

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

2
 
2
 
3
 import android.app.Activity;
3
 import android.app.Activity;
4
 import android.support.annotation.CallSuper;
4
 import android.support.annotation.CallSuper;
5
+import android.support.annotation.CheckResult;
5
 import android.support.annotation.NonNull;
6
 import android.support.annotation.NonNull;
6
 import android.support.annotation.Nullable;
7
 import android.support.annotation.Nullable;
7
 import android.support.v4.view.ViewPager;
8
 import android.support.v4.view.ViewPager;
9
 
10
 
10
 import com.reactnativenavigation.parse.Options;
11
 import com.reactnativenavigation.parse.Options;
11
 import com.reactnativenavigation.presentation.OptionsPresenter;
12
 import com.reactnativenavigation.presentation.OptionsPresenter;
13
+import com.reactnativenavigation.utils.CollectionUtils;
12
 import com.reactnativenavigation.views.Component;
14
 import com.reactnativenavigation.views.Component;
13
 
15
 
14
 import java.util.Collection;
16
 import java.util.Collection;
19
 		super(activity, childRegistry, id, presenter, initialOptions);
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
 	@Override
45
 	@Override
24
 	public T getView() {
46
 	public T getView() {
25
 		return (T) super.getView();
47
 		return (T) super.getView();

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

23
 	private ViewController leftController;
23
 	private ViewController leftController;
24
 	private ViewController rightController;
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
 	@Override
41
 	@Override
32
 	protected DrawerLayout createView() {
42
 	protected DrawerLayout createView() {
33
         return new DrawerLayout(getActivity());
43
         return new DrawerLayout(getActivity());

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

2
 
2
 
3
 import android.app.Activity;
3
 import android.app.Activity;
4
 import android.support.annotation.CallSuper;
4
 import android.support.annotation.CallSuper;
5
+import android.support.annotation.CheckResult;
5
 import android.support.annotation.NonNull;
6
 import android.support.annotation.NonNull;
6
 import android.support.annotation.Nullable;
7
 import android.support.annotation.Nullable;
7
 import android.support.annotation.VisibleForTesting;
8
 import android.support.annotation.VisibleForTesting;
74
         return false;
75
         return false;
75
     }
76
     }
76
 
77
 
78
+    @CheckResult
79
+    public Options resolveCurrentOptions() {
80
+        return options;
81
+    }
82
+
77
     @CallSuper
83
     @CallSuper
78
     public void mergeOptions(Options options) {
84
     public void mergeOptions(Options options) {
79
         this.options = this.options.mergeWith(options);
85
         this.options = this.options.mergeWith(options);
86
 
92
 
87
     }
93
     }
88
 
94
 
95
+    public void setDefaultOptions(Options defaultOptions) {
96
+        
97
+    }
98
+
89
     public Activity getActivity() {
99
     public Activity getActivity() {
90
         return activity;
100
         return activity;
91
     }
101
     }

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

10
 public class BottomTabFinder {
10
 public class BottomTabFinder {
11
     private List<ViewController> tabs;
11
     private List<ViewController> tabs;
12
 
12
 
13
+    public BottomTabFinder(List<ViewController> tabs) {
14
+        this.tabs = tabs;
15
+    }
16
+
13
     @IntRange(from = -1)
17
     @IntRange(from = -1)
14
     public int findByComponent(Component component) {
18
     public int findByComponent(Component component) {
15
         for (int i = 0; i < tabs.size(); i++) {
19
         for (int i = 0; i < tabs.size(); i++) {
29
         }
33
         }
30
         return -1;
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 View File

12
 import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
12
 import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
13
 import com.reactnativenavigation.parse.BottomTabOptions;
13
 import com.reactnativenavigation.parse.BottomTabOptions;
14
 import com.reactnativenavigation.parse.Options;
14
 import com.reactnativenavigation.parse.Options;
15
+import com.reactnativenavigation.presentation.BottomTabOptionsPresenter;
15
 import com.reactnativenavigation.presentation.BottomTabsOptionsPresenter;
16
 import com.reactnativenavigation.presentation.BottomTabsOptionsPresenter;
16
 import com.reactnativenavigation.presentation.OptionsPresenter;
17
 import com.reactnativenavigation.presentation.OptionsPresenter;
17
 import com.reactnativenavigation.react.EventEmitter;
18
 import com.reactnativenavigation.react.EventEmitter;
39
     private EventEmitter eventEmitter;
40
     private EventEmitter eventEmitter;
40
     private ImageLoader imageLoader;
41
     private ImageLoader imageLoader;
41
     private BottomTabsOptionsPresenter presenter;
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
         this.tabs = tabs;
47
         this.tabs = tabs;
47
         this.eventEmitter = eventEmitter;
48
         this.eventEmitter = eventEmitter;
48
         this.imageLoader = imageLoader;
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
 	@Override
62
 	@Override
54
 	protected ViewGroup createView() {
63
 	protected ViewGroup createView() {
55
 		RelativeLayout root = new RelativeLayout(getActivity());
64
 		RelativeLayout root = new RelativeLayout(getActivity());
56
 		bottomTabs = new BottomTabs(getActivity());
65
 		bottomTabs = new BottomTabs(getActivity());
57
-        presenter = new BottomTabsOptionsPresenter(bottomTabs, tabs, this, bottomTabFinder);
66
+        presenter.bindView(bottomTabs, this);
67
+        tabPresenter.bindView(bottomTabs);
58
         bottomTabs.setOnTabSelectedListener(this);
68
         bottomTabs.setOnTabSelectedListener(this);
59
 		RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
69
 		RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
60
 		lp.addRule(ALIGN_PARENT_BOTTOM);
70
 		lp.addRule(ALIGN_PARENT_BOTTOM);
61
 		root.addView(bottomTabs, lp);
71
 		root.addView(bottomTabs, lp);
72
+		createTabs(root);
62
 		return root;
73
 		return root;
63
 	}
74
 	}
64
 
75
 
66
     public void applyOptions(Options options) {
77
     public void applyOptions(Options options) {
67
         super.applyOptions(options);
78
         super.applyOptions(options);
68
         presenter.present(options);
79
         presenter.present(options);
80
+        tabPresenter.present();
69
     }
81
     }
70
 
82
 
71
     @Override
83
     @Override
81
     public void mergeChildOptions(Options options, Component child) {
93
     public void mergeChildOptions(Options options, Component child) {
82
         super.mergeChildOptions(options, child);
94
         super.mergeChildOptions(options, child);
83
         presenter.presentChildOptions(options, child);
95
         presenter.presentChildOptions(options, child);
96
+        tabPresenter.mergeChildOptions(options, child);
84
         applyOnParentController(parentController ->
97
         applyOnParentController(parentController ->
85
                 ((ParentController) parentController).mergeChildOptions(options.copy().clearBottomTabsOptions(), child)
98
                 ((ParentController) parentController).mergeChildOptions(options.copy().clearBottomTabsOptions(), child)
86
         );
99
         );
93
 
106
 
94
     @Override
107
     @Override
95
     public void sendOnNavigationButtonPressed(String buttonId) {
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
         return tabs.get(bottomTabs.getCurrentItem());
114
         return tabs.get(bottomTabs.getCurrentItem());
101
     }
115
     }
102
 
116
 
105
         if (wasSelected) return false;
119
         if (wasSelected) return false;
106
         eventEmitter.emitBottomTabSelected(bottomTabs.getCurrentItem(), index);
120
         eventEmitter.emitBottomTabSelected(bottomTabs.getCurrentItem(), index);
107
         selectTab(index);
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
 		if (tabs.size() > 5) {
126
 		if (tabs.size() > 5) {
113
 			throw new RuntimeException("Too many tabs!");
127
 			throw new RuntimeException("Too many tabs!");
114
 		}
128
 		}
115
-		this.tabs = tabs;
116
-        bottomTabFinder.setTabs(tabs);
117
-        getView();
118
         List<String> icons = new ArrayList<>();
129
         List<String> icons = new ArrayList<>();
119
         List<BottomTabOptions> bottomTabOptionsList = new ArrayList<>();
130
         List<BottomTabOptions> bottomTabOptionsList = new ArrayList<>();
120
         for (int i = 0; i < tabs.size(); i++) {
131
         for (int i = 0; i < tabs.size(); i++) {
133
             public void onComplete(@NonNull List<Drawable> drawables) {
144
             public void onComplete(@NonNull List<Drawable> drawables) {
134
                 List<AHBottomNavigationItem> tabs = new ArrayList<>();
145
                 List<AHBottomNavigationItem> tabs = new ArrayList<>();
135
                 for (int i = 0; i < drawables.size(); i++) {
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
                 bottomTabs.addItems(tabs);
149
                 bottomTabs.addItems(tabs);
139
                 bottomTabs.post(() -> {
150
                 bottomTabs.post(() -> {
141
                         bottomTabs.setTabTestId(i, bottomTabOptionsList.get(i).testId);
152
                         bottomTabs.setTabTestId(i, bottomTabOptionsList.get(i).testId);
142
                     }
153
                     }
143
                 });
154
                 });
144
-                attachTabs();
155
+                attachTabs(root);
145
             }
156
             }
146
 
157
 
147
             @Override
158
             @Override
149
                 error.printStackTrace();
160
                 error.printStackTrace();
150
             }
161
             }
151
         });
162
         });
152
-
153
 	}
163
 	}
154
 
164
 
155
-    private void attachTabs() {
165
+    private void attachTabs(RelativeLayout root) {
156
         for (int i = (tabs.size() - 1); i >= 0; i--) {
166
         for (int i = (tabs.size() - 1); i >= 0; i--) {
157
             ViewGroup tab = tabs.get(i).getView();
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
             if (i != 0) tab.setVisibility(View.INVISIBLE);
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 View File

1
 package com.reactnativenavigation.viewcontrollers.stack;
1
 package com.reactnativenavigation.viewcontrollers.stack;
2
 
2
 
3
 import com.reactnativenavigation.parse.Options;
3
 import com.reactnativenavigation.parse.Options;
4
+import com.reactnativenavigation.parse.params.Bool;
4
 import com.reactnativenavigation.viewcontrollers.ViewController;
5
 import com.reactnativenavigation.viewcontrollers.ViewController;
5
 
6
 
6
 public class BackButtonHelper {
7
 public class BackButtonHelper {
7
-    public void addToChild(StackController stack, ViewController child) {
8
+    public void addToPushedChild(StackController stack, ViewController child) {
8
         if (stack.size() <= 1 || child.options.topBar.buttons.left != null || child.options.topBar.buttons.back.visible.isFalse()) return;
9
         if (stack.size() <= 1 || child.options.topBar.buttons.left != null || child.options.topBar.buttons.back.visible.isFalse()) return;
9
         Options options = new Options();
10
         Options options = new Options();
10
         options.topBar.buttons.back.setVisible();
11
         options.topBar.buttons.back.setVisible();
11
         child.mergeOptions(options);
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 View File

5
 import android.support.annotation.RestrictTo;
5
 import android.support.annotation.RestrictTo;
6
 import android.support.annotation.VisibleForTesting;
6
 import android.support.annotation.VisibleForTesting;
7
 import android.support.v4.view.ViewPager;
7
 import android.support.v4.view.ViewPager;
8
+import android.view.ViewGroup;
9
+import android.widget.RelativeLayout;
8
 
10
 
9
 import com.reactnativenavigation.anim.NavigationAnimator;
11
 import com.reactnativenavigation.anim.NavigationAnimator;
10
 import com.reactnativenavigation.parse.Options;
12
 import com.reactnativenavigation.parse.Options;
11
 import com.reactnativenavigation.presentation.OptionsPresenter;
13
 import com.reactnativenavigation.presentation.OptionsPresenter;
14
+import com.reactnativenavigation.presentation.StackOptionsPresenter;
12
 import com.reactnativenavigation.react.Constants;
15
 import com.reactnativenavigation.react.Constants;
13
 import com.reactnativenavigation.utils.CommandListener;
16
 import com.reactnativenavigation.utils.CommandListener;
14
 import com.reactnativenavigation.utils.CommandListenerAdapter;
17
 import com.reactnativenavigation.utils.CommandListenerAdapter;
27
 
30
 
28
 import java.util.Collection;
31
 import java.util.Collection;
29
 import java.util.Iterator;
32
 import java.util.Iterator;
33
+import java.util.List;
30
 
34
 
31
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
35
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
32
 
36
 
39
     private TopBarBackgroundViewController topBarBackgroundViewController;
43
     private TopBarBackgroundViewController topBarBackgroundViewController;
40
     private TopBarController topBarController;
44
     private TopBarController topBarController;
41
     private BackButtonHelper backButtonHelper;
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
         this.topBarController = topBarController;
50
         this.topBarController = topBarController;
46
         this.topBarButtonCreator = topBarButtonCreator;
51
         this.topBarButtonCreator = topBarButtonCreator;
47
         this.titleBarReactViewCreator = titleBarReactViewCreator;
52
         this.titleBarReactViewCreator = titleBarReactViewCreator;
48
         this.topBarBackgroundViewController = topBarBackgroundViewController;
53
         this.topBarBackgroundViewController = topBarBackgroundViewController;
49
         this.animator = animator;
54
         this.animator = animator;
50
         this.backButtonHelper = backButtonHelper;
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
     @Override
74
     @Override
54
     public void applyChildOptions(Options options, Component child) {
75
     public void applyChildOptions(Options options, Component child) {
55
         super.applyChildOptions(options, child);
76
         super.applyChildOptions(options, child);
56
-        getView().applyChildOptions(this.options, child);
77
+        presenter.applyChildOptions(options, child);
57
         if (child instanceof ReactComponent) {
78
         if (child instanceof ReactComponent) {
58
             fabOptionsPresenter.applyOptions(this.options.fabOptions, (ReactComponent) child, getView());
79
             fabOptionsPresenter.applyOptions(this.options.fabOptions, (ReactComponent) child, getView());
59
         }
80
         }
74
     @Override
95
     @Override
75
     public void mergeChildOptions(Options options, Component child) {
96
     public void mergeChildOptions(Options options, Component child) {
76
         super.mergeChildOptions(options, child);
97
         super.mergeChildOptions(options, child);
77
-        getView().mergeChildOptions(options, child);
98
+        presenter.mergeChildOptions(options, child);
78
         animator.mergeOptions(options.animations);
99
         animator.mergeOptions(options.animations);
79
         if (options.fabOptions.hasValue() && child instanceof ReactComponent) {
100
         if (options.fabOptions.hasValue() && child instanceof ReactComponent) {
80
             fabOptionsPresenter.mergeOptions(options.fabOptions, (ReactComponent) child, getView());
101
             fabOptionsPresenter.mergeOptions(options.fabOptions, (ReactComponent) child, getView());
108
         final ViewController toRemove = stack.peek();
129
         final ViewController toRemove = stack.peek();
109
         child.setParentController(this);
130
         child.setParentController(this);
110
         stack.push(child.getId(), child);
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
         if (toRemove != null) {
138
         if (toRemove != null) {
115
             if (child.options.animations.push.enable.isTrueOrUndefined()) {
139
             if (child.options.animations.push.enable.isTrueOrUndefined()) {
126
         }
150
         }
127
     }
151
     }
128
 
152
 
129
-    private void addBackButton(ViewController child) {
130
-        backButtonHelper.addToChild(this, child);
131
-    }
132
-
133
     public void setRoot(ViewController child, CommandListener listener) {
153
     public void setRoot(ViewController child, CommandListener listener) {
154
+        backButtonHelper.clear(child);
134
         push(child, new CommandListenerAdapter() {
155
         push(child, new CommandListenerAdapter() {
135
             @Override
156
             @Override
136
             public void onSuccess(String childId) {
157
             public void onSuccess(String childId) {
160
         final ViewController appearing = stack.peek();
181
         final ViewController appearing = stack.peek();
161
         disappearing.onViewWillDisappear();
182
         disappearing.onViewWillDisappear();
162
         appearing.onViewWillAppear();
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
         if (disappearing.options.animations.pop.enable.isTrueOrUndefined()) {
192
         if (disappearing.options.animations.pop.enable.isTrueOrUndefined()) {
167
             animator.pop(disappearing.getView(), () -> finishPopping(disappearing, listener));
193
             animator.pop(disappearing.getView(), () -> finishPopping(disappearing, listener));
168
         } else {
194
         } else {
256
     @NonNull
282
     @NonNull
257
     @Override
283
     @Override
258
     protected StackLayout createView() {
284
     protected StackLayout createView() {
259
-        return new StackLayout(getActivity(),
285
+        StackLayout stackLayout = new StackLayout(getActivity(),
260
                 topBarButtonCreator,
286
                 topBarButtonCreator,
261
                 titleBarReactViewCreator,
287
                 titleBarReactViewCreator,
262
                 topBarBackgroundViewController,
288
                 topBarBackgroundViewController,
264
                 this::onNavigationButtonPressed,
290
                 this::onNavigationButtonPressed,
265
                 getId()
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
     private void onNavigationButtonPressed(String buttonId) {
307
     private void onNavigationButtonPressed(String buttonId) {

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

4
 
4
 
5
 import com.reactnativenavigation.anim.NavigationAnimator;
5
 import com.reactnativenavigation.anim.NavigationAnimator;
6
 import com.reactnativenavigation.parse.Options;
6
 import com.reactnativenavigation.parse.Options;
7
+import com.reactnativenavigation.presentation.OptionsPresenter;
8
+import com.reactnativenavigation.presentation.StackOptionsPresenter;
7
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
9
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
8
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
10
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
11
+import com.reactnativenavigation.viewcontrollers.ViewController;
9
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
12
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
10
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
13
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
11
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
14
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
12
 
15
 
16
+import java.util.ArrayList;
17
+import java.util.List;
18
+
13
 public class StackControllerBuilder {
19
 public class StackControllerBuilder {
14
     private Activity activity;
20
     private Activity activity;
15
     private ChildControllersRegistry childRegistry;
21
     private ChildControllersRegistry childRegistry;
21
     private Options initialOptions = new Options();
27
     private Options initialOptions = new Options();
22
     private NavigationAnimator animator;
28
     private NavigationAnimator animator;
23
     private BackButtonHelper backButtonHelper = new BackButtonHelper();
29
     private BackButtonHelper backButtonHelper = new BackButtonHelper();
30
+    private OptionsPresenter presenter;
31
+    private StackOptionsPresenter stackPresenter;
32
+    private List<ViewController> children = new ArrayList<>();
24
 
33
 
25
     public StackControllerBuilder(Activity activity) {
34
     public StackControllerBuilder(Activity activity) {
26
         this.activity = activity;
35
         this.activity = activity;
36
+        presenter = new OptionsPresenter(activity, new Options());
27
         animator = new NavigationAnimator(activity);
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
     public StackControllerBuilder setChildRegistry(ChildControllersRegistry childRegistry) {
55
     public StackControllerBuilder setChildRegistry(ChildControllersRegistry childRegistry) {
31
         this.childRegistry = childRegistry;
56
         this.childRegistry = childRegistry;
32
         return this;
57
         return this;
73
     }
98
     }
74
 
99
 
75
     public StackController build() {
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 View File

24
     private List<ViewController> tabs;
24
     private List<ViewController> tabs;
25
     private TopTabsLayoutCreator viewCreator;
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
         this.viewCreator = viewCreator;
29
         this.viewCreator = viewCreator;
30
         this.tabs = tabs;
30
         this.tabs = tabs;
31
         for (ViewController tab : tabs) {
31
         for (ViewController tab : tabs) {
39
         }
39
         }
40
     }
40
     }
41
 
41
 
42
+    @Override
43
+    protected ViewController getCurrentChild() {
44
+        return tabs.get(getView().getCurrentItem());
45
+    }
46
+
42
     @NonNull
47
     @NonNull
43
     @Override
48
     @Override
44
     protected TopTabsViewPager createView() {
49
     protected TopTabsViewPager createView() {

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

25
         if (BuildConfig.DEBUG) view.setContentDescription(testId.get());
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
     @Override
32
     @Override
34
         super.setCurrentItem(position);
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
     @Override
37
     @Override
48
     public void setTitleState(TitleState titleState) {
38
     public void setTitleState(TitleState titleState) {
49
         if (getTitleState() != titleState) super.setTitleState(titleState);
39
         if (getTitleState() != titleState) super.setTitleState(titleState);

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

9
 
9
 
10
 import com.reactnativenavigation.interfaces.ScrollEventListener;
10
 import com.reactnativenavigation.interfaces.ScrollEventListener;
11
 import com.reactnativenavigation.parse.Options;
11
 import com.reactnativenavigation.parse.Options;
12
+import com.reactnativenavigation.utils.UiUtils;
12
 import com.reactnativenavigation.utils.ViewUtils;
13
 import com.reactnativenavigation.utils.ViewUtils;
13
 import com.reactnativenavigation.viewcontrollers.IReactView;
14
 import com.reactnativenavigation.viewcontrollers.IReactView;
14
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
15
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
77
 
78
 
78
     @Override
79
     @Override
79
     public void drawBehindTopBar() {
80
     public void drawBehindTopBar() {
80
-        if (getParent() instanceof RelativeLayout) {
81
+        if (getLayoutParams() instanceof RelativeLayout.LayoutParams) {
81
             RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
82
             RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
82
             layoutParams.topMargin = 0;
83
             layoutParams.topMargin = 0;
83
             setLayoutParams(layoutParams);
84
             setLayoutParams(layoutParams);
86
 
87
 
87
     @Override
88
     @Override
88
     public void drawBelowTopBar(TopBar topBar) {
89
     public void drawBelowTopBar(TopBar topBar) {
89
-        if (getParent() instanceof RelativeLayout) {
90
+        if (getLayoutParams() instanceof RelativeLayout.LayoutParams) {
90
             RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
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
             setLayoutParams(layoutParams);
98
             setLayoutParams(layoutParams);
93
         }
99
         }
94
     }
100
     }

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

4
 import android.content.Context;
4
 import android.content.Context;
5
 import android.widget.RelativeLayout;
5
 import android.widget.RelativeLayout;
6
 
6
 
7
-import com.reactnativenavigation.parse.Options;
8
-import com.reactnativenavigation.presentation.StackOptionsPresenter;
9
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
7
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
10
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
8
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
11
-import com.reactnativenavigation.viewcontrollers.ViewController;
12
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
9
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
13
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
10
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
14
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
11
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
19
 @SuppressLint("ViewConstructor")
16
 @SuppressLint("ViewConstructor")
20
 public class StackLayout extends RelativeLayout {
17
 public class StackLayout extends RelativeLayout {
21
     private String stackId;
18
     private String stackId;
22
-    private final StackOptionsPresenter optionsPresenter;
23
 
19
 
24
     public StackLayout(Context context, ReactViewCreator topBarButtonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, TopBarButtonController.OnClickListener topBarButtonClickListener, String stackId) {
20
     public StackLayout(Context context, ReactViewCreator topBarButtonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarController topBarController, TopBarButtonController.OnClickListener topBarButtonClickListener, String stackId) {
25
         super(context);
21
         super(context);
26
         this.stackId = stackId;
22
         this.stackId = stackId;
27
         createLayout(topBarButtonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarController, topBarButtonClickListener);
23
         createLayout(topBarButtonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarController, topBarButtonClickListener);
28
-        optionsPresenter = new StackOptionsPresenter(topBarController.getView());
29
         setContentDescription("StackLayout");
24
         setContentDescription("StackLayout");
30
     }
25
     }
31
 
26
 
33
         addView(topBarController.createView(getContext(), buttonCreator, titleBarReactViewCreator, topBarBackgroundViewController, topBarButtonClickListener, this), MATCH_PARENT, WRAP_CONTENT);
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
     public String getStackId() {
31
     public String getStackId() {
53
         return stackId;
32
         return stackId;
54
     }
33
     }

+ 17
- 1
lib/android/app/src/test/java/com/reactnativenavigation/TestUtils.java View File

1
 package com.reactnativenavigation;
1
 package com.reactnativenavigation;
2
 
2
 
3
 import android.app.Activity;
3
 import android.app.Activity;
4
+import android.content.Context;
4
 
5
 
5
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
6
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
6
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
7
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
7
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
8
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
8
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.parse.Options;
10
+import com.reactnativenavigation.presentation.StackOptionsPresenter;
11
+import com.reactnativenavigation.utils.ImageLoader;
9
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
12
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
13
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
14
+import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
10
 import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
15
 import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
11
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
16
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
12
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
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
 public class TestUtils {
22
 public class TestUtils {
15
     public static StackControllerBuilder newStackController(Activity activity) {
23
     public static StackControllerBuilder newStackController(Activity activity) {
19
                 .setTopBarButtonCreator(new TopBarButtonCreatorMock())
27
                 .setTopBarButtonCreator(new TopBarButtonCreatorMock())
20
                 .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
28
                 .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
21
                 .setTopBarBackgroundViewController(new TopBarBackgroundViewController(activity, new TopBarBackgroundViewCreatorMock()))
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
                 .setInitialOptions(new Options());
39
                 .setInitialOptions(new Options());
24
     }
40
     }
25
 }
41
 }

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

3
 import android.app.*;
3
 import android.app.*;
4
 
4
 
5
 import com.reactnativenavigation.parse.*;
5
 import com.reactnativenavigation.parse.*;
6
+import com.reactnativenavigation.presentation.OptionsPresenter;
6
 import com.reactnativenavigation.viewcontrollers.*;
7
 import com.reactnativenavigation.viewcontrollers.*;
7
 
8
 
8
 public class SimpleComponentViewController extends ComponentViewController {
9
 public class SimpleComponentViewController extends ComponentViewController {
9
     public SimpleComponentViewController(Activity activity, ChildControllersRegistry childRegistry, String id, Options initialOptions) {
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 View File

6
 import android.view.MotionEvent;
6
 import android.view.MotionEvent;
7
 import android.view.View;
7
 import android.view.View;
8
 import android.widget.FrameLayout;
8
 import android.widget.FrameLayout;
9
+import android.widget.RelativeLayout;
9
 
10
 
10
 import com.reactnativenavigation.interfaces.ScrollEventListener;
11
 import com.reactnativenavigation.interfaces.ScrollEventListener;
11
 import com.reactnativenavigation.parse.Options;
12
 import com.reactnativenavigation.parse.Options;
12
 import com.reactnativenavigation.presentation.OptionsPresenter;
13
 import com.reactnativenavigation.presentation.OptionsPresenter;
14
+import com.reactnativenavigation.utils.ViewUtils;
13
 import com.reactnativenavigation.viewcontrollers.ChildController;
15
 import com.reactnativenavigation.viewcontrollers.ChildController;
14
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
16
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
15
 import com.reactnativenavigation.views.ReactComponent;
17
 import com.reactnativenavigation.views.ReactComponent;
20
     private SimpleView simpleView;
22
     private SimpleView simpleView;
21
 
23
 
22
     public SimpleViewController(Activity activity, ChildControllersRegistry childRegistry, String id, Options options) {
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
     public SimpleViewController(Activity activity, ChildControllersRegistry childRegistry, String id, OptionsPresenter presenter, Options options) {
28
     public SimpleViewController(Activity activity, ChildControllersRegistry childRegistry, String id, OptionsPresenter presenter, Options options) {
57
 
59
 
58
         @Override
60
         @Override
59
         public void drawBehindTopBar() {
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
         @Override
70
         @Override
64
         public void drawBelowTopBar(TopBar topBar) {
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
         @Override
80
         @Override

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

269
     @Test
269
     @Test
270
     public void clear_bottomTabsOptions() {
270
     public void clear_bottomTabsOptions() {
271
         Options uut = new Options();
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
         uut.clearBottomTabsOptions();
273
         uut.clearBottomTabsOptions();
274
-        assertThat(uut.bottomTabsOptions.tabColor.hasValue()).isFalse();
274
+        assertThat(uut.bottomTabsOptions.backgroundColor.hasValue()).isFalse();
275
     }
275
     }
276
 
276
 
277
     @Test
277
     @Test

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

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

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

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 View File

15
 import com.reactnativenavigation.parse.params.Color;
15
 import com.reactnativenavigation.parse.params.Color;
16
 import com.reactnativenavigation.parse.params.Number;
16
 import com.reactnativenavigation.parse.params.Number;
17
 import com.reactnativenavigation.parse.params.Text;
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
 import com.reactnativenavigation.react.EventEmitter;
22
 import com.reactnativenavigation.react.EventEmitter;
19
 import com.reactnativenavigation.utils.CommandListenerAdapter;
23
 import com.reactnativenavigation.utils.CommandListenerAdapter;
20
 import com.reactnativenavigation.utils.ImageLoader;
24
 import com.reactnativenavigation.utils.ImageLoader;
33
 
37
 
34
 import static org.assertj.core.api.Java6Assertions.assertThat;
38
 import static org.assertj.core.api.Java6Assertions.assertThat;
35
 import static org.mockito.ArgumentMatchers.any;
39
 import static org.mockito.ArgumentMatchers.any;
40
+import static org.mockito.ArgumentMatchers.eq;
36
 import static org.mockito.Mockito.spy;
41
 import static org.mockito.Mockito.spy;
37
 import static org.mockito.Mockito.times;
42
 import static org.mockito.Mockito.times;
38
 import static org.mockito.Mockito.verify;
43
 import static org.mockito.Mockito.verify;
47
     private ViewController child3;
52
     private ViewController child3;
48
     private StackController child4;
53
     private StackController child4;
49
     private ViewController child5;
54
     private ViewController child5;
55
+    private ViewController child6;
50
     private Options tabOptions = OptionHelper.createBottomTabOptions();
56
     private Options tabOptions = OptionHelper.createBottomTabOptions();
51
     private ImageLoader imageLoaderMock = ImageLoaderMock.mock();
57
     private ImageLoader imageLoaderMock = ImageLoaderMock.mock();
52
     private EventEmitter eventEmitter;
58
     private EventEmitter eventEmitter;
53
     private ChildControllersRegistry childRegistry;
59
     private ChildControllersRegistry childRegistry;
54
     private List<ViewController> tabs;
60
     private List<ViewController> tabs;
61
+    private BottomTabsOptionsPresenter presenter;
55
 
62
 
56
     @Override
63
     @Override
57
     public void beforeEach() {
64
     public void beforeEach() {
64
         child3 = spy(new SimpleViewController(activity, childRegistry, "child3", tabOptions));
71
         child3 = spy(new SimpleViewController(activity, childRegistry, "child3", tabOptions));
65
         child4 = spy(createStack("someStack"));
72
         child4 = spy(createStack("someStack"));
66
         child5 = spy(new SimpleViewController(activity, childRegistry, "child5", tabOptions));
73
         child5 = spy(new SimpleViewController(activity, childRegistry, "child5", tabOptions));
74
+        child6 = spy(new SimpleViewController(activity, childRegistry, "child6", tabOptions));
67
         when(child5.handleBack(any())).thenReturn(true);
75
         when(child5.handleBack(any())).thenReturn(true);
68
         tabs = createTabs();
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
     @Test
81
     @Test
86
     @Test(expected = RuntimeException.class)
87
     @Test(expected = RuntimeException.class)
87
     public void setTabs_ThrowWhenMoreThan5() {
88
     public void setTabs_ThrowWhenMoreThan5() {
88
         tabs.add(new SimpleViewController(activity, childRegistry, "6", tabOptions));
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
     @Test
93
     @Test
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
     @Test
120
     @Test
124
     public void onTabSelected() {
121
     public void onTabSelected() {
122
+        uut.ensureViewIsCreated();
125
         assertThat(uut.getSelectedIndex()).isZero();
123
         assertThat(uut.getSelectedIndex()).isZero();
126
         assertThat(((ViewController) ((List) uut.getChildControllers()).get(0)).getView().getVisibility()).isEqualTo(View.VISIBLE);
124
         assertThat(((ViewController) ((List) uut.getChildControllers()).get(0)).getView().getVisibility()).isEqualTo(View.VISIBLE);
127
 
125
 
135
 
133
 
136
     @Test
134
     @Test
137
     public void onTabReSelected() {
135
     public void onTabReSelected() {
136
+        uut.ensureViewIsCreated();
138
         assertThat(uut.getSelectedIndex()).isZero();
137
         assertThat(uut.getSelectedIndex()).isZero();
139
 
138
 
140
         uut.onTabSelected(0, false);
139
         uut.onTabSelected(0, false);
146
 
145
 
147
     @Test
146
     @Test
148
     public void handleBack_DelegatesToSelectedChild() {
147
     public void handleBack_DelegatesToSelectedChild() {
148
+        uut.ensureViewIsCreated();
149
         assertThat(uut.handleBack(new CommandListenerAdapter())).isFalse();
149
         assertThat(uut.handleBack(new CommandListenerAdapter())).isFalse();
150
         uut.selectTab(4);
150
         uut.selectTab(4);
151
         assertThat(uut.handleBack(new CommandListenerAdapter())).isTrue();
151
         assertThat(uut.handleBack(new CommandListenerAdapter())).isTrue();
155
     @Test
155
     @Test
156
     public void applyOptions_bottomTabsOptionsAreClearedAfterApply() {
156
     public void applyOptions_bottomTabsOptionsAreClearedAfterApply() {
157
         Options options = new Options();
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
         child1.mergeOptions(options);
159
         child1.mergeOptions(options);
160
         uut.ensureViewIsCreated();
160
         uut.ensureViewIsCreated();
161
 
161
 
168
         ArgumentCaptor<ReactComponent> viewCaptor = ArgumentCaptor.forClass(ReactComponent.class);
168
         ArgumentCaptor<ReactComponent> viewCaptor = ArgumentCaptor.forClass(ReactComponent.class);
169
         verify(stack, times(1)).applyChildOptions(optionsCaptor.capture(), viewCaptor.capture());
169
         verify(stack, times(1)).applyChildOptions(optionsCaptor.capture(), viewCaptor.capture());
170
         assertThat(viewCaptor.getValue()).isEqualTo(child1.getView());
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
     @Test
174
     @Test
226
 
226
 
227
     @Test
227
     @Test
228
     public void push() {
228
     public void push() {
229
+        uut.ensureViewIsCreated();
229
         uut.selectTab(3);
230
         uut.selectTab(3);
230
 
231
 
231
         SimpleViewController stackChild = new SimpleViewController(activity, childRegistry, "stackChild", new Options());
232
         SimpleViewController stackChild = new SimpleViewController(activity, childRegistry, "stackChild", new Options());
238
         assertThat(child4.size()).isEqualTo(2);
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
     @NonNull
261
     @NonNull
242
     private List<ViewController> createTabs() {
262
     private List<ViewController> createTabs() {
243
         return Arrays.asList(child1, child2, child3, child4, child5);
263
         return Arrays.asList(child1, child2, child3, child4, child5);
247
         return TestUtils.newStackController(activity)
267
         return TestUtils.newStackController(activity)
248
                 .setId(id)
268
                 .setId(id)
249
                 .setInitialOptions(tabOptions)
269
                 .setInitialOptions(tabOptions)
270
+                .setStackPresenter(new StackOptionsPresenter(activity, new Options()))
250
                 .build();
271
                 .build();
251
     }
272
     }
252
 
273
 
253
     private ViewGroup.MarginLayoutParams childLayoutParams(int index) {
274
     private ViewGroup.MarginLayoutParams childLayoutParams(int index) {
254
         return (ViewGroup.MarginLayoutParams) tabs.get(index).getView().getLayoutParams();
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 View File

7
 import com.reactnativenavigation.mocks.TestComponentLayout;
7
 import com.reactnativenavigation.mocks.TestComponentLayout;
8
 import com.reactnativenavigation.mocks.TestReactView;
8
 import com.reactnativenavigation.mocks.TestReactView;
9
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.parse.Options;
10
+import com.reactnativenavigation.presentation.OptionsPresenter;
10
 import com.reactnativenavigation.views.StackLayout;
11
 import com.reactnativenavigation.views.StackLayout;
11
 
12
 
12
 import org.junit.Test;
13
 import org.junit.Test;
27
         Activity activity = newActivity();
28
         Activity activity = newActivity();
28
         view = spy(new TestComponentLayout(activity, new TestReactView(activity)));
29
         view = spy(new TestComponentLayout(activity, new TestReactView(activity)));
29
         ParentController<StackLayout> parentController = TestUtils.newStackController(activity).build();
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
         uut.setParentController(parentController);
33
         uut.setParentController(parentController);
32
         parentController.ensureViewIsCreated();
34
         parentController.ensureViewIsCreated();
33
     }
35
     }

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

37
         activity = newActivity();
37
         activity = newActivity();
38
         childRegistry = new ChildControllersRegistry();
38
         childRegistry = new ChildControllersRegistry();
39
         stackController = TestUtils.newStackController(activity).build();
39
         stackController = TestUtils.newStackController(activity).build();
40
+        stackController.ensureViewIsCreated();
40
         Options options = getOptionsWithFab();
41
         Options options = getOptionsWithFab();
41
         childFab = new SimpleViewController(activity, childRegistry, "child1", options);
42
         childFab = new SimpleViewController(activity, childRegistry, "child1", options);
42
         childNoFab = new SimpleViewController(activity, childRegistry, "child2", new Options());
43
         childNoFab = new SimpleViewController(activity, childRegistry, "child2", new Options());

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

1
 package com.reactnativenavigation.viewcontrollers;
1
 package com.reactnativenavigation.viewcontrollers;
2
 
2
 
3
 import android.support.annotation.NonNull;
3
 import android.support.annotation.NonNull;
4
+import android.view.View;
4
 
5
 
5
 import com.reactnativenavigation.BaseTest;
6
 import com.reactnativenavigation.BaseTest;
6
 import com.reactnativenavigation.TestActivity;
7
 import com.reactnativenavigation.TestActivity;
11
 import com.reactnativenavigation.parse.Options;
12
 import com.reactnativenavigation.parse.Options;
12
 import com.reactnativenavigation.parse.params.Bool;
13
 import com.reactnativenavigation.parse.params.Bool;
13
 import com.reactnativenavigation.parse.params.Text;
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
 import com.reactnativenavigation.presentation.OverlayManager;
18
 import com.reactnativenavigation.presentation.OverlayManager;
15
 import com.reactnativenavigation.react.EventEmitter;
19
 import com.reactnativenavigation.react.EventEmitter;
16
 import com.reactnativenavigation.utils.CommandListener;
20
 import com.reactnativenavigation.utils.CommandListener;
50
     private ActivityController<TestActivity> activityController;
54
     private ActivityController<TestActivity> activityController;
51
     private OverlayManager overlayManager;
55
     private OverlayManager overlayManager;
52
     private EventEmitter eventEmitter;
56
     private EventEmitter eventEmitter;
57
+    private ViewController.ViewVisibilityListener parentVisibilityListener;
53
 
58
 
54
     @Override
59
     @Override
55
     public void beforeEach() {
60
     public void beforeEach() {
62
         uut = new Navigator(activity, childRegistry, overlayManager);
67
         uut = new Navigator(activity, childRegistry, overlayManager);
63
         activity.setNavigator(uut);
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
         child1 = new SimpleViewController(activity, childRegistry, "child1", tabOptions);
84
         child1 = new SimpleViewController(activity, childRegistry, "child1", tabOptions);
68
         child2 = new SimpleViewController(activity, childRegistry, "child2", tabOptions);
85
         child2 = new SimpleViewController(activity, childRegistry, "child2", tabOptions);
69
         child3 = new SimpleViewController(activity, childRegistry, "child3", tabOptions);
86
         child3 = new SimpleViewController(activity, childRegistry, "child3", tabOptions);
74
         activityController.visible();
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
     @Test
105
     @Test
78
     public void setRoot_AddsChildControllerView() {
106
     public void setRoot_AddsChildControllerView() {
79
         assertThat(uut.getContentLayout().getChildCount()).isZero();
107
         assertThat(uut.getContentLayout().getChildCount()).isZero();
118
 
146
 
119
     @Test
147
     @Test
120
     public void push_OnCorrectStackByFindingChildId() {
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
         stack1.push(child1, new CommandListenerAdapter());
151
         stack1.push(child1, new CommandListenerAdapter());
124
         stack2.push(child2, new CommandListenerAdapter());
152
         stack2.push(child2, new CommandListenerAdapter());
125
         BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
153
         BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
165
 
193
 
166
     @Test
194
     @Test
167
     public void popSpecific() {
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
         stack1.push(child1, new CommandListenerAdapter());
199
         stack1.push(child1, new CommandListenerAdapter());
171
         stack2.push(child2, new CommandListenerAdapter());
200
         stack2.push(child2, new CommandListenerAdapter());
172
         stack2.push(child3, new CommandListenerAdapter());
201
         stack2.push(child3, new CommandListenerAdapter());
271
 
300
 
272
     @NonNull
301
     @NonNull
273
     private BottomTabsController newTabs(List<ViewController> tabs) {
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
     @NonNull
306
     @NonNull
278
     private StackController newStack() {
307
     private StackController newStack() {
279
-        return TestUtils.newStackController(activity)
308
+        StackController stack = TestUtils.newStackController(activity)
280
                 .setChildRegistry(childRegistry)
309
                 .setChildRegistry(childRegistry)
281
                 .setId("stack" + CompatUtils.generateViewId())
310
                 .setId("stack" + CompatUtils.generateViewId())
282
                 .setInitialOptions(tabOptions)
311
                 .setInitialOptions(tabOptions)
283
                 .build();
312
                 .build();
313
+        stack.ensureViewIsCreated();
314
+        return stack;
284
     }
315
     }
285
 
316
 
286
     @Test
317
     @Test
358
     public void pushedStackCanBePopped() {
389
     public void pushedStackCanBePopped() {
359
         child1.options.animations.push.enable = new Bool(false);
390
         child1.options.animations.push.enable = new Bool(false);
360
         child2.options.animations.push.enable = new Bool(false);
391
         child2.options.animations.push.enable = new Bool(false);
392
+        StackController spy = spy(parentController);
361
         StackController parent = newStack();
393
         StackController parent = newStack();
362
         parent.ensureViewIsCreated();
394
         parent.ensureViewIsCreated();
363
         uut.setRoot(parent, new CommandListenerAdapter());
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
         child1.ensureViewIsCreated();
401
         child1.ensureViewIsCreated();
370
         child2.ensureViewIsCreated();
402
         child2.ensureViewIsCreated();
371
 
403
 
372
         CommandListenerAdapter listener = new CommandListenerAdapter() {
404
         CommandListenerAdapter listener = new CommandListenerAdapter() {
373
             @Override
405
             @Override
374
             public void onSuccess(String childId) {
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
         uut.popSpecific("child2", listener);
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
     @Test
414
     @Test
406
 
438
 
407
         uut.dismissModal(child2.getId(), new CommandListenerAdapter());
439
         uut.dismissModal(child2.getId(), new CommandListenerAdapter());
408
         assertThat(parentController.getView().getParent()).isNull();
440
         assertThat(parentController.getView().getParent()).isNull();
409
-        verify(parentController, times(1)).onViewAppeared();
441
+        verify(parentVisibilityListener, times(1)).onViewAppeared(parentController.getView());
410
 
442
 
411
         uut.dismissModal(child1.getId(), new CommandListenerAdapter());
443
         uut.dismissModal(child1.getId(), new CommandListenerAdapter());
412
         assertThat(parentController.getView().getParent()).isNotNull();
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
     @Test
449
     @Test
419
         disableShowModalAnimation(child1);
451
         disableShowModalAnimation(child1);
420
 
452
 
421
         uut.dismissAllModals(new CommandListenerAdapter());
453
         uut.dismissAllModals(new CommandListenerAdapter());
422
-        verify(parentController, times(0)).onViewAppeared();
454
+        verify(parentVisibilityListener, times(0)).onViewAppeared(parentController.getView());
423
 
455
 
424
         uut.setRoot(parentController, new CommandListenerAdapter());
456
         uut.setRoot(parentController, new CommandListenerAdapter());
425
-        verify(parentController, times(1)).onViewAppeared();
457
+
458
+        verify(parentVisibilityListener, times(1)).onViewAppeared(parentController.getView());
426
         uut.showModal(child1, new CommandListenerAdapter());
459
         uut.showModal(child1, new CommandListenerAdapter());
427
         uut.dismissAllModals(new CommandListenerAdapter());
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
     @Test
465
     @Test
433
     public void handleBack_onViewAppearedInvokedOnRoot() {
466
     public void handleBack_onViewAppearedInvokedOnRoot() {
434
         disableShowModalAnimation(child1, child2);
467
         disableShowModalAnimation(child1, child2);
435
 
468
 
436
-        uut.setRoot(parentController, new CommandListenerAdapter());
469
+        StackController spy = spy(parentController);
470
+        uut.setRoot(spy, new CommandListenerAdapter());
437
         uut.showModal(child1, new CommandListenerAdapter());
471
         uut.showModal(child1, new CommandListenerAdapter());
438
         uut.showModal(child2, new CommandListenerAdapter());
472
         uut.showModal(child2, new CommandListenerAdapter());
439
 
473
 
440
         uut.handleBack(new CommandListenerAdapter());
474
         uut.handleBack(new CommandListenerAdapter());
441
-        verify(parentController, times(1)).onViewAppeared();
475
+        verify(parentVisibilityListener, times(1)).onViewAppeared(spy.getView());
442
 
476
 
443
         uut.handleBack(new CommandListenerAdapter() {
477
         uut.handleBack(new CommandListenerAdapter() {
444
             @Override
478
             @Override
445
             public void onSuccess(String childId) {
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
     @Test
486
     @Test
453
     public void destroy_destroyedRoot() {
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
         activityController.destroy();
491
         activityController.destroy();
457
-        verify(parentController, times(1)).destroy();
492
+        verify(spy, times(1)).destroy();
458
     }
493
     }
459
 
494
 
460
     @Test
495
     @Test
476
 
511
 
477
     @Test
512
     @Test
478
     public void reload_navigatorIsDestroyedOnReload() {
513
     public void reload_navigatorIsDestroyedOnReload() {
479
-        uut.setRoot(parentController, new CommandListenerAdapter());
514
+        StackController spy = spy(parentController);
515
+        uut.setRoot(spy, new CommandListenerAdapter());
480
         uut.onReload();
516
         uut.onReload();
481
-        verify(parentController, times(1)).destroy();
517
+        verify(spy, times(1)).destroy();
482
         verify(overlayManager, times(1)).destroy();
518
         verify(overlayManager, times(1)).destroy();
483
     }
519
     }
484
 }
520
 }

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

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

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

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

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

7
 import com.reactnativenavigation.mocks.SimpleComponentViewController;
7
 import com.reactnativenavigation.mocks.SimpleComponentViewController;
8
 import com.reactnativenavigation.parse.Options;
8
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.parse.params.Bool;
9
 import com.reactnativenavigation.parse.params.Bool;
10
+import com.reactnativenavigation.presentation.OptionsPresenter;
10
 
11
 
11
 import org.junit.Test;
12
 import org.junit.Test;
12
 
13
 
21
     public void beforeEach() {
22
     public void beforeEach() {
22
         activity = newActivity();
23
         activity = newActivity();
23
         childRegistry = new ChildControllersRegistry();
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
     @Test
29
     @Test

lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsMergingTest.java → lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackOptionsPresenterTest.java View File

33
 import static org.mockito.Mockito.verify;
33
 import static org.mockito.Mockito.verify;
34
 import static org.mockito.Mockito.when;
34
 import static org.mockito.Mockito.when;
35
 
35
 
36
-public class OptionsMergingTest extends BaseTest {
36
+public class StackOptionsPresenterTest extends BaseTest {
37
 
37
 
38
     private StackOptionsPresenter uut;
38
     private StackOptionsPresenter uut;
39
     private TestComponentLayout child;
39
     private TestComponentLayout child;
43
     @Override
43
     @Override
44
     public void beforeEach() {
44
     public void beforeEach() {
45
         activity = spy(newActivity());
45
         activity = spy(newActivity());
46
+        uut = spy(new StackOptionsPresenter(activity, new Options()));
46
         topBar = mockTopBar();
47
         topBar = mockTopBar();
47
-        uut = spy(new StackOptionsPresenter(topBar));
48
+        uut.bindView(topBar);
48
         child = spy(new TestComponentLayout(activity, new TestReactView(activity)));
49
         child = spy(new TestComponentLayout(activity, new TestReactView(activity)));
49
     }
50
     }
50
 
51
 
80
     public void mergeTopBarOptions() {
81
     public void mergeTopBarOptions() {
81
         Options options = new Options();
82
         Options options = new Options();
82
         uut.mergeChildOptions(options, child);
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
         SubtitleOptions subtitleOptions = new SubtitleOptions();
94
         SubtitleOptions subtitleOptions = new SubtitleOptions();
94
         subtitleOptions.text = new Text("Sub");
95
         subtitleOptions.text = new Text("Sub");
95
         subtitleOptions.color = new Color(1);
96
         subtitleOptions.color = new Color(1);
102
         options.topBar.hideOnScroll = new Bool(false);
103
         options.topBar.hideOnScroll = new Bool(false);
103
         uut.mergeChildOptions(options, child);
104
         uut.mergeChildOptions(options, child);
104
 
105
 
105
-        assertTopBarOptions(1);
106
+        assertTopBarOptions(options, 1);
106
 
107
 
107
         options.topBar.drawBehind = new Bool(true);
108
         options.topBar.drawBehind = new Bool(true);
108
         uut.mergeChildOptions(options, child);
109
         uut.mergeChildOptions(options, child);
141
         verify(topBar, times(1)).setTopTabFontFamily(1, Typeface.DEFAULT_BOLD);
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
         verify(topBar, times(t)).setTitleComponent(any());
153
         verify(topBar, times(t)).setTitleComponent(any());
148
         verify(topBar, times(t)).setBackgroundColor(any());
154
         verify(topBar, times(t)).setBackgroundColor(any());
149
         verify(topBar, times(t)).setTitleTextColor(anyInt());
155
         verify(topBar, times(t)).setTitleTextColor(anyInt());

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

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

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

75
 
75
 
76
         assertThat(uut.getParentController()).isNull();
76
         assertThat(uut.getParentController()).isNull();
77
         StackController nav = TestUtils.newStackController(activity).build();
77
         StackController nav = TestUtils.newStackController(activity).build();
78
+        nav.ensureViewIsCreated();
78
         nav.push(uut, new CommandListenerAdapter());
79
         nav.push(uut, new CommandListenerAdapter());
79
         assertThat(uut.getParentController()).isEqualTo(nav);
80
         assertThat(uut.getParentController()).isEqualTo(nav);
80
     }
81
     }

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

37
                 .build();
37
                 .build();
38
         child1 = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
38
         child1 = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
39
         child2 = spy(new SimpleViewController(activity, childRegistry, "child2", new Options()));
39
         child2 = spy(new SimpleViewController(activity, childRegistry, "child2", new Options()));
40
+        stack.ensureViewIsCreated();
40
     }
41
     }
41
 
42
 
42
     @Test
43
     @Test
43
     public void addToChild_doesNotAddIfStackContainsOneChild() {
44
     public void addToChild_doesNotAddIfStackContainsOneChild() {
44
-        uut.addToChild(stack, child1);
45
+        uut.addToPushedChild(stack, child1);
45
         verify(child1, times(0)).mergeOptions(any());
46
         verify(child1, times(0)).mergeOptions(any());
46
     }
47
     }
47
 
48
 
65
 
66
 
66
         verify(child2, times(0)).mergeOptions(any());
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 View File

3
 import android.app.Activity;
3
 import android.app.Activity;
4
 import android.content.Context;
4
 import android.content.Context;
5
 import android.view.View;
5
 import android.view.View;
6
+import android.view.ViewGroup;
6
 
7
 
7
 import com.reactnativenavigation.BaseTest;
8
 import com.reactnativenavigation.BaseTest;
8
 import com.reactnativenavigation.TestUtils;
9
 import com.reactnativenavigation.TestUtils;
17
 import com.reactnativenavigation.parse.params.Bool;
18
 import com.reactnativenavigation.parse.params.Bool;
18
 import com.reactnativenavigation.parse.params.Button;
19
 import com.reactnativenavigation.parse.params.Button;
19
 import com.reactnativenavigation.parse.params.Text;
20
 import com.reactnativenavigation.parse.params.Text;
21
+import com.reactnativenavigation.presentation.StackOptionsPresenter;
20
 import com.reactnativenavigation.utils.CommandListenerAdapter;
22
 import com.reactnativenavigation.utils.CommandListenerAdapter;
21
 import com.reactnativenavigation.utils.ImageLoader;
23
 import com.reactnativenavigation.utils.ImageLoader;
22
 import com.reactnativenavigation.utils.ViewHelper;
24
 import com.reactnativenavigation.utils.ViewHelper;
40
 import org.mockito.Mockito;
42
 import org.mockito.Mockito;
41
 
43
 
42
 import java.util.ArrayList;
44
 import java.util.ArrayList;
45
+import java.util.Arrays;
43
 import java.util.Collections;
46
 import java.util.Collections;
47
+import java.util.List;
44
 
48
 
45
 import static org.assertj.core.api.Java6Assertions.assertThat;
49
 import static org.assertj.core.api.Java6Assertions.assertThat;
46
 import static org.mockito.ArgumentMatchers.any;
50
 import static org.mockito.ArgumentMatchers.any;
61
     private ViewController child4;
65
     private ViewController child4;
62
     private NavigationAnimator animator;
66
     private NavigationAnimator animator;
63
     private TopBarController topBarController;
67
     private TopBarController topBarController;
68
+    private StackOptionsPresenter presenter;
64
 
69
 
65
     @Override
70
     @Override
66
     public void beforeEach() {
71
     public void beforeEach() {
68
         animator = Mockito.mock(NavigationAnimator.class);
73
         animator = Mockito.mock(NavigationAnimator.class);
69
         activity = newActivity();
74
         activity = newActivity();
70
         childRegistry = new ChildControllersRegistry();
75
         childRegistry = new ChildControllersRegistry();
71
-        uut = createStackController();
76
+        presenter = new StackOptionsPresenter(activity, new Options());
72
         child1 = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
77
         child1 = spy(new SimpleViewController(activity, childRegistry, "child1", new Options()));
73
         child2 = spy(new SimpleViewController(activity, childRegistry, "child2", new Options()));
78
         child2 = spy(new SimpleViewController(activity, childRegistry, "child2", new Options()));
74
         child3 = spy(new SimpleViewController(activity, childRegistry, "child3", new Options()));
79
         child3 = spy(new SimpleViewController(activity, childRegistry, "child3", new Options()));
75
         child4 = spy(new SimpleViewController(activity, childRegistry, "child4", new Options()));
80
         child4 = spy(new SimpleViewController(activity, childRegistry, "child4", new Options()));
81
+        uut = createStack();
82
+        uut.ensureViewIsCreated();
76
     }
83
     }
77
 
84
 
78
     @Test
85
     @Test
80
         assertThat(uut).isInstanceOf(ViewController.class);
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
     @Test
106
     @Test
84
     public void holdsAStackOfViewControllers() {
107
     public void holdsAStackOfViewControllers() {
85
         assertThat(uut.isEmpty()).isTrue();
108
         assertThat(uut.isEmpty()).isTrue();
137
 
160
 
138
     @Test
161
     @Test
139
     public void animateSetRoot() {
162
     public void animateSetRoot() {
163
+        disablePushAnimation(child1, child2, child3);
140
         assertThat(uut.isEmpty()).isTrue();
164
         assertThat(uut.isEmpty()).isTrue();
141
         uut.push(child1, new CommandListenerAdapter());
165
         uut.push(child1, new CommandListenerAdapter());
142
         uut.push(child2, new CommandListenerAdapter());
166
         uut.push(child2, new CommandListenerAdapter());
150
 
174
 
151
     @Test
175
     @Test
152
     public void setRoot() {
176
     public void setRoot() {
153
-        disablePushAnimation(child1, child2);
177
+        activity.setContentView(uut.getView());
178
+        disablePushAnimation(child1, child2, child3);
179
+
154
         assertThat(uut.isEmpty()).isTrue();
180
         assertThat(uut.isEmpty()).isTrue();
155
         uut.push(child1, new CommandListenerAdapter());
181
         uut.push(child1, new CommandListenerAdapter());
156
         uut.push(child2, new CommandListenerAdapter());
182
         uut.push(child2, new CommandListenerAdapter());
165
     }
191
     }
166
 
192
 
167
     @Test
193
     @Test
168
-    public void pop() {
194
+    public synchronized void pop() {
195
+        disablePushAnimation(child1, child2);
169
         uut.push(child1, new CommandListenerAdapter());
196
         uut.push(child1, new CommandListenerAdapter());
170
         uut.push(child2, new CommandListenerAdapter() {
197
         uut.push(child2, new CommandListenerAdapter() {
171
             @Override
198
             @Override
191
 
218
 
192
     @Test
219
     @Test
193
     public void pop_layoutHandlesChildWillDisappear() {
220
     public void pop_layoutHandlesChildWillDisappear() {
194
-        final StackLayout[] stackLayout = new StackLayout[1];
195
         uut = new StackControllerBuilder(activity)
221
         uut = new StackControllerBuilder(activity)
196
                         .setTopBarButtonCreator(new TopBarButtonCreatorMock())
222
                         .setTopBarButtonCreator(new TopBarButtonCreatorMock())
197
                         .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
223
                         .setTitleBarReactViewCreator(new TitleBarReactViewCreatorMock())
199
                         .setTopBarController(new TopBarController())
225
                         .setTopBarController(new TopBarController())
200
                         .setId("uut")
226
                         .setId("uut")
201
                         .setInitialOptions(new Options())
227
                         .setInitialOptions(new Options())
228
+                        .setStackPresenter(new StackOptionsPresenter(activity, new Options()))
202
                         .build();
229
                         .build();
230
+        uut.ensureViewIsCreated();
203
         uut.push(child1, new CommandListenerAdapter());
231
         uut.push(child1, new CommandListenerAdapter());
204
         uut.push(child2, new CommandListenerAdapter() {
232
         uut.push(child2, new CommandListenerAdapter() {
205
             @Override
233
             @Override
207
                 uut.pop(new CommandListenerAdapter() {
235
                 uut.pop(new CommandListenerAdapter() {
208
                     @Override
236
                     @Override
209
                     public void onSuccess(String childId) {
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
         uut.push(child1, new CommandListenerAdapter());
324
         uut.push(child1, new CommandListenerAdapter());
297
         assertThat(child1.getParentController()).isEqualTo(uut);
325
         assertThat(child1.getParentController()).isEqualTo(uut);
298
 
326
 
299
-        StackController anotherNavController = createStackController("another");
327
+        StackController anotherNavController = createStack("another");
328
+        anotherNavController.ensureViewIsCreated();
300
         anotherNavController.push(child2, new CommandListenerAdapter());
329
         anotherNavController.push(child2, new CommandListenerAdapter());
301
         assertThat(child2.getParentController()).isEqualTo(anotherNavController);
330
         assertThat(child2.getParentController()).isEqualTo(anotherNavController);
302
     }
331
     }
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
     @Test
428
     @Test
381
     public void popSpecific_deepInStack() {
429
     public void popSpecific_deepInStack() {
382
         uut.push(child1, new CommandListenerAdapter());
430
         uut.push(child1, new CommandListenerAdapter());
517
 
565
 
518
     @Test
566
     @Test
519
     public void findControllerById_Deeply() {
567
     public void findControllerById_Deeply() {
520
-        StackController stack = createStackController("another");
568
+        StackController stack = createStack("another");
569
+        stack.ensureViewIsCreated();
521
         stack.push(child2, new CommandListenerAdapter());
570
         stack.push(child2, new CommandListenerAdapter());
522
         uut.push(stack, new CommandListenerAdapter());
571
         uut.push(stack, new CommandListenerAdapter());
523
         assertThat(uut.findControllerById(child2.getId())).isEqualTo(child2);
572
         assertThat(uut.findControllerById(child2.getId())).isEqualTo(child2);
643
 
692
 
644
     @Test
693
     @Test
645
     public void stackCanBePushed() {
694
     public void stackCanBePushed() {
646
-        StackController parent = createStackController("someStack");
695
+        StackController parent = createStack("someStack");
647
         parent.ensureViewIsCreated();
696
         parent.ensureViewIsCreated();
648
         parent.push(uut, new CommandListenerAdapter());
697
         parent.push(uut, new CommandListenerAdapter());
649
         uut.onViewAppeared();
698
         uut.onViewAppeared();
652
 
701
 
653
     @Test
702
     @Test
654
     public void applyOptions_applyOnlyOnFirstStack() {
703
     public void applyOptions_applyOnlyOnFirstStack() {
655
-        StackController parent = spy(createStackController("someStack"));
704
+        StackController parent = spy(createStack("someStack"));
656
         parent.ensureViewIsCreated();
705
         parent.ensureViewIsCreated();
657
         parent.push(uut, new CommandListenerAdapter());
706
         parent.push(uut, new CommandListenerAdapter());
658
 
707
 
695
                         .setTopBarController(new TopBarController())
744
                         .setTopBarController(new TopBarController())
696
                         .setId("stack")
745
                         .setId("stack")
697
                         .setInitialOptions(new Options())
746
                         .setInitialOptions(new Options())
747
+                        .setStackPresenter(new StackOptionsPresenter(activity, new Options()))
698
                         .build());
748
                         .build());
699
         Options optionsToMerge = new Options();
749
         Options optionsToMerge = new Options();
700
         Component component = mock(Component.class);
750
         Component component = mock(Component.class);
711
                         .setTopBarController(new TopBarController())
761
                         .setTopBarController(new TopBarController())
712
                         .setId("stack")
762
                         .setId("stack")
713
                         .setInitialOptions(new Options())
763
                         .setInitialOptions(new Options())
764
+                        .setStackPresenter(new StackOptionsPresenter(activity, new Options()))
714
                         .build();
765
                         .build();
715
         ParentController parentController = Mockito.mock(ParentController.class);
766
         ParentController parentController = Mockito.mock(ParentController.class);
716
         uut.setParentController(parentController);
767
         uut.setParentController(parentController);
768
+        uut.ensureViewIsCreated();
717
         Options optionsToMerge = new Options();
769
         Options optionsToMerge = new Options();
718
         optionsToMerge.topBar.testId = new Text("topBarID");
770
         optionsToMerge.topBar.testId = new Text("topBarID");
719
         optionsToMerge.bottomTabsOptions.testId = new Text("bottomTabsID");
771
         optionsToMerge.bottomTabsOptions.testId = new Text("bottomTabsID");
738
 
790
 
739
     @Test
791
     @Test
740
     public void mergeChildOptions_StackRelatedOptionsAreCleared() {
792
     public void mergeChildOptions_StackRelatedOptionsAreCleared() {
793
+        uut.ensureViewIsCreated();
741
         ParentController parentController = Mockito.mock(ParentController.class);
794
         ParentController parentController = Mockito.mock(ParentController.class);
742
         uut.setParentController(parentController);
795
         uut.setParentController(parentController);
743
         Options options = new Options();
796
         Options options = new Options();
767
         assertThat(uut.getChildControllers()).extracting((Extractor<ViewController, String>) ViewController::getId).containsOnly(ids);
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
         createTopBarController();
836
         createTopBarController();
776
         return TestUtils.newStackController(activity)
837
         return TestUtils.newStackController(activity)
838
+                .setChildren(children)
777
                 .setId(id)
839
                 .setId(id)
778
                 .setTopBarController(topBarController)
840
                 .setTopBarController(topBarController)
779
                 .setChildRegistry(childRegistry)
841
                 .setChildRegistry(childRegistry)
780
                 .setAnimator(animator)
842
                 .setAnimator(animator)
843
+                .setStackPresenter(presenter)
781
                 .build();
844
                 .build();
782
     }
845
     }
783
 
846
 
785
         topBarController = spy(new TopBarController() {
848
         topBarController = spy(new TopBarController() {
786
             @Override
849
             @Override
787
             protected TopBar createTopBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarBackgroundViewController topBarBackgroundViewController, TopBarButtonController.OnClickListener topBarButtonClickListener, StackLayout stackLayout, ImageLoader imageLoader) {
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 View File

3
 @interface RNNBottomTabOptions : RNNOptions
3
 @interface RNNBottomTabOptions : RNNOptions
4
 
4
 
5
 @property (nonatomic) NSUInteger tag;
5
 @property (nonatomic) NSUInteger tag;
6
-@property (nonatomic, strong) NSString* title;
6
+@property (nonatomic, strong) NSString* text;
7
 @property (nonatomic, strong) NSString* badge;
7
 @property (nonatomic, strong) NSString* badge;
8
 @property (nonatomic, strong) NSString* testID;
8
 @property (nonatomic, strong) NSString* testID;
9
 @property (nonatomic, strong) NSNumber* visible;
9
 @property (nonatomic, strong) NSNumber* visible;
10
 @property (nonatomic, strong) NSDictionary* icon;
10
 @property (nonatomic, strong) NSDictionary* icon;
11
 @property (nonatomic, strong) NSDictionary* selectedIcon;
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
 @property (nonatomic, strong) NSDictionary* iconInsets;
19
 @property (nonatomic, strong) NSDictionary* iconInsets;
15
 
20
 
16
 @end
21
 @end

+ 83
- 23
lib/ios/RNNBottomTabOptions.m View File

1
 #import "RNNBottomTabOptions.h"
1
 #import "RNNBottomTabOptions.h"
2
+#import "UIImage+tint.h"
2
 
3
 
3
 @implementation RNNBottomTabOptions
4
 @implementation RNNBottomTabOptions
4
 
5
 
12
 }
13
 }
13
 
14
 
14
 - (void)applyOn:(UIViewController *)viewController {
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
 		tabItem.accessibilityIdentifier = self.testID;
23
 		tabItem.accessibilityIdentifier = self.testID;
24
 		
24
 		
25
 		if (self.iconInsets && ![self.iconInsets isKindOfClass:[NSNull class]]) {
25
 		if (self.iconInsets && ![self.iconInsets isKindOfClass:[NSNull class]]) {
36
 			tabItem.imageInsets = UIEdgeInsetsMake(top, left, bottom, right);
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
 	if (self.badge) {
44
 	if (self.badge) {
71
 	[self resetOptions];
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
 -(void)resetOptions {
133
 -(void)resetOptions {
75
-	self.title = nil;
134
+	self.text = nil;
76
 	self.badge = nil;
135
 	self.badge = nil;
77
 	self.visible = nil;
136
 	self.visible = nil;
78
 	self.icon = nil;
137
 	self.icon = nil;
79
 	self.testID = nil;
138
 	self.testID = nil;
80
 	self.iconInsets = nil;
139
 	self.iconInsets = nil;
140
+	self.selectedIcon = nil;
81
 }
141
 }
82
 
142
 
83
 @end
143
 @end

+ 2
- 2
lib/ios/RNNBottomTabsOptions.h View File

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

+ 1
- 18
lib/ios/RNNBottomTabsOptions.m View File

48
 	if (self.hideShadow) {
48
 	if (self.hideShadow) {
49
 		viewController.tabBarController.tabBar.clipsToBounds = [self.hideShadow boolValue];
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
 	[self resetOptions];
52
 	[self resetOptions];
70
 }
53
 }
71
 
54
 

+ 3
- 2
lib/ios/RNNControllerFactory.m View File

233
 }
233
 }
234
 
234
 
235
 - (RNNNavigationOptions *)createOptions:(NSDictionary *)optionsDict {
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
 	return options;
239
 	return options;
239
 }
240
 }
240
 
241
 

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

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

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

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

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

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

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

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

+ 12
- 23
lib/ios/ReactNativeNavigationTests/RNNRootViewControllerTest.m View File

671
 	XCTAssertTrue([self.uut.tabBarController.tabBar.barTintColor isEqual:expectedColor]);
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
 -(void)testTabBarTextFontFamily_validFont{
674
 -(void)testTabBarTextFontFamily_validFont{
691
 	NSString* inputFont = @"HelveticaNeue";
675
 	NSString* inputFont = @"HelveticaNeue";
692
-	self.options.bottomTabs.fontFamily = inputFont;
676
+	self.options.bottomTab.fontFamily = inputFont;
677
+	self.options.bottomTab.text = @"Tab 1";
693
 	[self.uut embedInTabBarController];
678
 	[self.uut embedInTabBarController];
694
 	UIFont* expectedFont = [UIFont fontWithName:inputFont size:10];
679
 	UIFont* expectedFont = [UIFont fontWithName:inputFont size:10];
695
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];
680
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];
697
 }
682
 }
698
 
683
 
699
 -(void)testTabBarTextFontSize_withoutTextFontFamily_withoutTextColor {
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
 	[self.uut embedInTabBarController];
687
 	[self.uut embedInTabBarController];
702
 	UIFont* expectedFont = [UIFont systemFontOfSize:15];
688
 	UIFont* expectedFont = [UIFont systemFontOfSize:15];
703
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];
689
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];
705
 }
691
 }
706
 
692
 
707
 -(void)testTabBarTextFontSize_withoutTextFontFamily {
693
 -(void)testTabBarTextFontSize_withoutTextFontFamily {
708
-	self.options.bottomTabs.fontSize = @(15);
694
+	self.options.bottomTab.fontSize = @(15);
695
+	self.options.bottomTab.text = @"Tab 1";
709
 	[self.uut embedInTabBarController];
696
 	[self.uut embedInTabBarController];
710
 	UIFont* expectedFont = [UIFont systemFontOfSize:15];
697
 	UIFont* expectedFont = [UIFont systemFontOfSize:15];
711
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];
698
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];
714
 
701
 
715
 -(void)testTabBarTextFontSize_withTextFontFamily_withTextColor {
702
 -(void)testTabBarTextFontSize_withTextFontFamily_withTextColor {
716
 	NSString* inputFont = @"HelveticaNeue";
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
 	[self.uut embedInTabBarController];
707
 	[self.uut embedInTabBarController];
720
 	UIFont* expectedFont = [UIFont fontWithName:inputFont size:15];
708
 	UIFont* expectedFont = [UIFont fontWithName:inputFont size:15];
721
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];
709
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];
724
 
712
 
725
 -(void)testTabBarTextFontSize_withTextFontFamily_withoutTextColor {
713
 -(void)testTabBarTextFontSize_withTextFontFamily_withoutTextColor {
726
 	NSString* inputFont = @"HelveticaNeue";
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
 	[self.uut embedInTabBarController];
718
 	[self.uut embedInTabBarController];
730
 	UIFont* expectedFont = [UIFont fontWithName:inputFont size:15];
719
 	UIFont* expectedFont = [UIFont fontWithName:inputFont size:15];
731
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];
720
 	NSDictionary* attributes = [self.uut.tabBarController.tabBar.items.firstObject titleTextAttributesForState:UIControlStateNormal];

+ 7
- 0
lib/ios/UIImage+tint.h View File

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 View File

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 View File

25
             // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
25
             // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
26
             url "$rootDir/../../node_modules/react-native/android"
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 View File

24
   registerScreens();
24
   registerScreens();
25
   Navigation.events().registerAppLaunchedListener(() => {
25
   Navigation.events().registerAppLaunchedListener(() => {
26
     Navigation.setDefaultOptions({
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
       _animations: {
35
       _animations: {
28
         startApp: {
36
         startApp: {
29
           y: {
37
           y: {

+ 3
- 5
playground/src/screens/TextScreen.js View File

25
         <Text style={styles.h1} testID={testIDs.CENTERED_TEXT_HEADER}>{this.props.text || 'Text Screen'}</Text>
25
         <Text style={styles.h1} testID={testIDs.CENTERED_TEXT_HEADER}>{this.props.text || 'Text Screen'}</Text>
26
         {this.renderTextFromFunctionInProps()}
26
         {this.renderTextFromFunctionInProps()}
27
         <Text style={styles.footer}>{`this.props.componentId = ${this.props.componentId}`}</Text>
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
         <Button title={'Switch To Tab 2'} testID={testIDs.SWITCH_SECOND_TAB_BUTTON} onPress={() => this.onClickSwitchToTab()} />
29
         <Button title={'Switch To Tab 2'} testID={testIDs.SWITCH_SECOND_TAB_BUTTON} onPress={() => this.onClickSwitchToTab()} />
30
         <Button title={'Switch To Tab 1 by componentID'} testID={testIDs.SWITCH_FIRST_TAB_BUTTON} onPress={() => this.onClickSwitchToTabByComponentID()} />
30
         <Button title={'Switch To Tab 1 by componentID'} testID={testIDs.SWITCH_FIRST_TAB_BUTTON} onPress={() => this.onClickSwitchToTabByComponentID()} />
31
         <Button title='Hide Tab Bar' testID={testIDs.HIDE_BOTTOM_TABS_BUTTON} onPress={() => this.hideTabBar(false)} />
31
         <Button title='Hide Tab Bar' testID={testIDs.HIDE_BOTTOM_TABS_BUTTON} onPress={() => this.hideTabBar(false)} />
59
     );
59
     );
60
   }
60
   }
61
 
61
 
62
-  onButtonPress() {
62
+  onClickSetBadge() {
63
     Navigation.mergeOptions(this.props.componentId, {
63
     Navigation.mergeOptions(this.props.componentId, {
64
       bottomTab: {
64
       bottomTab: {
65
         badge: `TeSt`
65
         badge: `TeSt`
73
         currentTabIndex: 1,
73
         currentTabIndex: 1,
74
         visible: false,
74
         visible: false,
75
         drawBehind: true,
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 View File

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