Browse Source

Bottom tabs android (#2649)

* Use AHBottomNavigation for BottomTabs

* Process passed options for all layout types

Static options are still processed only for components

* ios e2e fix

* Update RNNNavigationController.m
Guy Carmeli 6 years ago
parent
commit
990e0594d1
No account linked to committer's email address
45 changed files with 417 additions and 211 deletions
  1. 2
    1
      lib/android/app/build.gradle
  2. 39
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabOptions.java
  3. 24
    16
      lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabsOptions.java
  4. 8
    4
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java
  5. 5
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java
  6. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabsOptions.java
  7. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/utils/CompatUtils.java
  8. 4
    4
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageLoader.java
  9. 51
    29
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/BottomTabsController.java
  10. 2
    3
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java
  11. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java
  12. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java
  13. 4
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/SideMenuController.java
  14. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java
  15. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java
  16. 1
    3
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java
  17. 13
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/BottomTabs.java
  18. 3
    3
      lib/android/app/src/main/java/com/reactnativenavigation/views/TitleBarButton.java
  19. 49
    0
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/ImageLoaderMock.java
  20. 2
    3
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleViewController.java
  21. 10
    12
      lib/android/app/src/test/java/com/reactnativenavigation/parse/NavigationOptionsTest.java
  22. 13
    0
      lib/android/app/src/test/java/com/reactnativenavigation/utils/OptionHelper.java
  23. 37
    25
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsControllerTest.java
  24. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ComponentViewControllerTest.java
  25. 31
    20
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java
  26. 2
    2
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java
  27. 10
    9
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ParentControllerTest.java
  28. 7
    6
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackControllerTest.java
  29. 0
    25
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsControllerMock.java
  30. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java
  31. 5
    4
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ViewControllerTest.java
  32. 2
    0
      lib/ios/RNNBottomTabsOptions.h
  33. 4
    1
      lib/ios/RNNControllerFactory.m
  34. 4
    0
      lib/ios/RNNNavigationController.m
  35. 3
    0
      lib/ios/RNNRootViewProtocol.h
  36. 4
    0
      lib/ios/RNNTabBarController.m
  37. 7
    2
      lib/src/commands/LayoutTreeCrawler.ts
  38. BIN
      playground/src/images/one@2x.png
  39. BIN
      playground/src/images/one_selected@2x.png
  40. BIN
      playground/src/images/three@2x.png
  41. BIN
      playground/src/images/three_selected@2x.png
  42. BIN
      playground/src/images/two@2x.png
  43. BIN
      playground/src/images/two_selected@2x.png
  44. 1
    1
      playground/src/screens/StaticLifecycleOverlay.js
  45. 59
    25
      playground/src/screens/WelcomeScreen.js

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

59
     implementation fileTree(include: ['*.jar'], dir: 'libs')
59
     implementation fileTree(include: ['*.jar'], dir: 'libs')
60
     implementation 'com.android.support:design:25.4.0'
60
     implementation 'com.android.support:design:25.4.0'
61
     implementation 'com.android.support:appcompat-v7:25.4.0'
61
     implementation 'com.android.support:appcompat-v7:25.4.0'
62
-    implementation "com.android.support:support-v4:25.4.0"
62
+    implementation 'com.android.support:support-v4:25.4.0'
63
+    implementation 'com.aurelhubert:ahbottomnavigation:2.1.0'
63
 
64
 
64
     // node_modules
65
     // node_modules
65
     //noinspection GradleDynamicVersion
66
     //noinspection GradleDynamicVersion

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

1
+package com.reactnativenavigation.parse;
2
+
3
+import org.json.JSONObject;
4
+
5
+public class BottomTabOptions implements DEFAULT_VALUES {
6
+
7
+    public static BottomTabOptions parse(JSONObject json) {
8
+        BottomTabOptions options = new BottomTabOptions();
9
+        if (json == null) return options;
10
+
11
+        options.title = TextParser.parse(json, "title");
12
+        if (!json.has("icon")) {
13
+            throw new RuntimeException("BottomTab must have an icon");
14
+        }
15
+        options.icon = TextParser.parse(json.optJSONObject("icon"), "uri");
16
+        return options;
17
+    }
18
+
19
+    public Text title = new NullText();
20
+    public Text icon = new NullText();
21
+
22
+    void mergeWith(final BottomTabOptions other) {
23
+        if (other.title.hasValue()) {
24
+            title = other.title;
25
+        }
26
+        if (other.icon.hasValue()) {
27
+            icon = other.icon;
28
+        }
29
+    }
30
+
31
+    void mergeWithDefault(final BottomTabOptions defaultOptions) {
32
+        if (!title.hasValue()) {
33
+            title = defaultOptions.title;
34
+        }
35
+        if (!icon.hasValue()) {
36
+            icon = defaultOptions.icon;
37
+        }
38
+    }
39
+}

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

11
 		BottomTabsOptions options = new BottomTabsOptions();
11
 		BottomTabsOptions options = new BottomTabsOptions();
12
 		if (json == null) return options;
12
 		if (json == null) return options;
13
 
13
 
14
-		options.currentTabId = TextParser.parse(json, "currentTabId");
14
+        options.color = ColorParser.parse(json, "tabColor");
15
+        options.selectedColor = ColorParser.parse(json, "selectedTabColor");
16
+        options.currentTabId = TextParser.parse(json, "currentTabId");
15
 		options.currentTabIndex = json.optInt("currentTabIndex", NO_INT_VALUE);
17
 		options.currentTabIndex = json.optInt("currentTabIndex", NO_INT_VALUE);
16
-		options.tabBadge = json.optInt("tabBadge", NO_INT_VALUE);
17
-		options.hidden = BooleanOptions.parse(json.optString("hidden"));
18
+		options.visible = BooleanOptions.parse(json.optString("visible"));
18
 		options.animateHide = BooleanOptions.parse(json.optString("animateHide"));
19
 		options.animateHide = BooleanOptions.parse(json.optString("animateHide"));
19
 
20
 
20
 		return options;
21
 		return options;
21
 	}
22
 	}
22
 
23
 
23
-	int tabBadge = NO_INT_VALUE;
24
-	BooleanOptions hidden = BooleanOptions.False;
24
+    public Color color = new NullColor();
25
+    public Color selectedColor = new NullColor();
26
+	BooleanOptions visible = BooleanOptions.False;
25
 	BooleanOptions animateHide = BooleanOptions.False;
27
 	BooleanOptions animateHide = BooleanOptions.False;
26
 	public int currentTabIndex = NO_INT_VALUE;
28
 	public int currentTabIndex = NO_INT_VALUE;
27
 	public Text currentTabId = new NullText();
29
 	public Text currentTabId = new NullText();
33
 		if (NO_INT_VALUE != other.currentTabIndex) {
35
 		if (NO_INT_VALUE != other.currentTabIndex) {
34
             currentTabIndex = other.currentTabIndex;
36
             currentTabIndex = other.currentTabIndex;
35
 		}
37
 		}
36
-		if (NO_INT_VALUE != other.tabBadge) {
37
-			tabBadge = other.tabBadge;
38
-		}
39
-		if (other.hidden != BooleanOptions.NoValue) {
40
-			hidden = other.hidden;
38
+		if (other.visible != BooleanOptions.NoValue) {
39
+			visible = other.visible;
41
 		}
40
 		}
42
 		if (other.animateHide != BooleanOptions.NoValue) {
41
 		if (other.animateHide != BooleanOptions.NoValue) {
43
 			animateHide = other.animateHide;
42
 			animateHide = other.animateHide;
44
 		}
43
 		}
45
-	}
44
+        if (other.color.hasValue()) {
45
+            color = other.color;
46
+        }
47
+        if (other.selectedColor.hasValue()) {
48
+            selectedColor = other.selectedColor;
49
+        }
50
+    }
46
 
51
 
47
     void mergeWithDefault(final BottomTabsOptions defaultOptions) {
52
     void mergeWithDefault(final BottomTabsOptions defaultOptions) {
48
         if (!currentTabId.hasValue()) {
53
         if (!currentTabId.hasValue()) {
51
         if (NO_INT_VALUE == currentTabIndex) {
56
         if (NO_INT_VALUE == currentTabIndex) {
52
             currentTabIndex = defaultOptions.currentTabIndex;
57
             currentTabIndex = defaultOptions.currentTabIndex;
53
         }
58
         }
54
-        if (NO_INT_VALUE == tabBadge) {
55
-            tabBadge = defaultOptions.tabBadge;
56
-        }
57
-        if (hidden == BooleanOptions.NoValue) {
58
-            hidden = defaultOptions.hidden;
59
+        if (visible == BooleanOptions.NoValue) {
60
+            visible = defaultOptions.visible;
59
         }
61
         }
60
         if (animateHide == BooleanOptions.NoValue) {
62
         if (animateHide == BooleanOptions.NoValue) {
61
             animateHide = defaultOptions.animateHide;
63
             animateHide = defaultOptions.animateHide;
62
         }
64
         }
65
+        if (!color.hasValue()) {
66
+            color = defaultOptions.color;
67
+        }
68
+        if (!selectedColor.hasValue()) {
69
+            selectedColor = defaultOptions.selectedColor;
70
+        }
63
     }
71
     }
64
 }
72
 }

+ 8
- 4
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.utils.ImageLoader;
6
 import com.reactnativenavigation.utils.NoOpPromise;
7
 import com.reactnativenavigation.utils.NoOpPromise;
7
 import com.reactnativenavigation.utils.TypefaceLoader;
8
 import com.reactnativenavigation.utils.TypefaceLoader;
8
 import com.reactnativenavigation.viewcontrollers.BottomTabsController;
9
 import com.reactnativenavigation.viewcontrollers.BottomTabsController;
55
 	}
56
 	}
56
 
57
 
57
     private ViewController createSideMenuRoot(LayoutNode node) {
58
     private ViewController createSideMenuRoot(LayoutNode node) {
58
-		SideMenuController sideMenuLayout = new SideMenuController(activity, node.id);
59
+        final Options options = Options.parse(typefaceManager, node.getNavigationOptions(), defaultOptions);
60
+		SideMenuController sideMenuLayout = new SideMenuController(activity, node.id, options);
59
 		for (LayoutNode child : node.children) {
61
 		for (LayoutNode child : node.children) {
60
 			ViewController childLayout = create(child);
62
 			ViewController childLayout = create(child);
61
 			switch (child.type) {
63
 			switch (child.type) {
100
 	}
102
 	}
101
 
103
 
102
 	private ViewController createStack(LayoutNode node) {
104
 	private ViewController createStack(LayoutNode node) {
103
-		StackController stackController = new StackController(activity, node.id);
105
+        final Options options = Options.parse(typefaceManager, node.getNavigationOptions(), defaultOptions);
106
+		StackController stackController = new StackController(activity, node.id, options);
104
         for (int i = 0; i < node.children.size(); i++) {
107
         for (int i = 0; i < node.children.size(); i++) {
105
             if (i < node.children.size() - 1) {
108
             if (i < node.children.size() - 1) {
106
                 stackController.push(create(node.children.get(i)), new NoOpPromise());
109
                 stackController.push(create(node.children.get(i)), new NoOpPromise());
112
 	}
115
 	}
113
 
116
 
114
 	private ViewController createBottomTabs(LayoutNode node) {
117
 	private ViewController createBottomTabs(LayoutNode node) {
115
-		final BottomTabsController tabsComponent = new BottomTabsController(activity, node.id);
118
+        final Options options = Options.parse(typefaceManager, node.getNavigationOptions(), defaultOptions);
119
+		final BottomTabsController tabsComponent = new BottomTabsController(activity, new ImageLoader(), node.id, options);
116
 		List<ViewController> tabs = new ArrayList<>();
120
 		List<ViewController> tabs = new ArrayList<>();
117
 		for (int i = 0; i < node.children.size(); i++) {
121
 		for (int i = 0; i < node.children.size(); i++) {
118
-			tabs.add(create(node.children.get(i)));
122
+            tabs.add(create(node.children.get(i)));
119
 		}
123
 		}
120
 		tabsComponent.setTabs(tabs);
124
 		tabsComponent.setTabs(tabs);
121
 		return tabsComponent;
125
 		return tabsComponent;

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

35
 		result.topBarOptions = TopBarOptions.parse(typefaceManager, json.optJSONObject("topBar"));
35
 		result.topBarOptions = TopBarOptions.parse(typefaceManager, json.optJSONObject("topBar"));
36
 		result.topTabsOptions = TopTabsOptions.parse(json.optJSONObject("topTabs"));
36
 		result.topTabsOptions = TopTabsOptions.parse(json.optJSONObject("topTabs"));
37
         result.topTabOptions = TopTabOptions.parse(typefaceManager, json.optJSONObject("topTab"));
37
         result.topTabOptions = TopTabOptions.parse(typefaceManager, json.optJSONObject("topTab"));
38
-		result.bottomTabsOptions = BottomTabsOptions.parse(json.optJSONObject("bottomTabs"));
38
+        result.bottomTabOptions = BottomTabOptions.parse(json.optJSONObject("bottomTab"));
39
+        result.bottomTabsOptions = BottomTabsOptions.parse(json.optJSONObject("bottomTabs"));
39
         result.overlayOptions = OverlayOptions.parse(json.optJSONObject("overlay"));
40
         result.overlayOptions = OverlayOptions.parse(json.optJSONObject("overlay"));
40
 
41
 
41
 		return result.withDefaultOptions(defaultOptions);
42
 		return result.withDefaultOptions(defaultOptions);
44
     @NonNull public TopBarOptions topBarOptions = new TopBarOptions();
45
     @NonNull public TopBarOptions topBarOptions = new TopBarOptions();
45
     @NonNull public TopTabsOptions topTabsOptions = new TopTabsOptions();
46
     @NonNull public TopTabsOptions topTabsOptions = new TopTabsOptions();
46
     @NonNull public TopTabOptions topTabOptions = new TopTabOptions();
47
     @NonNull public TopTabOptions topTabOptions = new TopTabOptions();
48
+    @NonNull public BottomTabOptions bottomTabOptions = new BottomTabOptions();
47
     @NonNull public BottomTabsOptions bottomTabsOptions = new BottomTabsOptions();
49
     @NonNull public BottomTabsOptions bottomTabsOptions = new BottomTabsOptions();
48
     @NonNull public OverlayOptions overlayOptions = new OverlayOptions();
50
     @NonNull public OverlayOptions overlayOptions = new OverlayOptions();
49
 
51
 
54
 	public void mergeWith(final Options other) {
56
 	public void mergeWith(final Options other) {
55
         topBarOptions.mergeWith(other.topBarOptions);
57
         topBarOptions.mergeWith(other.topBarOptions);
56
         topTabsOptions.mergeWith(other.topTabsOptions);
58
         topTabsOptions.mergeWith(other.topTabsOptions);
59
+        bottomTabOptions.mergeWith(other.bottomTabOptions);
57
         bottomTabsOptions.mergeWith(other.bottomTabsOptions);
60
         bottomTabsOptions.mergeWith(other.bottomTabsOptions);
58
     }
61
     }
59
 
62
 
60
     Options withDefaultOptions(final Options other) {
63
     Options withDefaultOptions(final Options other) {
61
         topBarOptions.mergeWithDefault(other.topBarOptions);
64
         topBarOptions.mergeWithDefault(other.topBarOptions);
62
         topTabsOptions.mergeWithDefault(other.topTabsOptions);
65
         topTabsOptions.mergeWithDefault(other.topTabsOptions);
66
+        bottomTabOptions.mergeWithDefault(other.bottomTabOptions);
63
         bottomTabsOptions.mergeWithDefault(other.bottomTabsOptions);
67
         bottomTabsOptions.mergeWithDefault(other.bottomTabsOptions);
64
         return this;
68
         return this;
65
     }
69
     }

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

14
     public static TopTabsOptions parse(@Nullable JSONObject json) {
14
     public static TopTabsOptions parse(@Nullable JSONObject json) {
15
         TopTabsOptions result = new TopTabsOptions();
15
         TopTabsOptions result = new TopTabsOptions();
16
         if (json == null) return result;
16
         if (json == null) return result;
17
-        result.selectedTabColor = ColorParser.parse(json, "selectedTabColor");
17
+        result.selectedTabColor = ColorParser.parse(json, "selectedColor");
18
         result.unselectedTabColor = ColorParser.parse(json, "unselectedTabColor");
18
         result.unselectedTabColor = ColorParser.parse(json, "unselectedTabColor");
19
         result.fontSize = NumberParser.parse(json, "fontSize");
19
         result.fontSize = NumberParser.parse(json, "fontSize");
20
         return result;
20
         return result;

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

9
 	private static final AtomicInteger viewId = new AtomicInteger(1);
9
 	private static final AtomicInteger viewId = new AtomicInteger(1);
10
 
10
 
11
 	public static int generateViewId() {
11
 	public static int generateViewId() {
12
-		if (Build.VERSION.SDK_INT >= 17) {
12
+		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
13
 			return View.generateViewId();
13
 			return View.generateViewId();
14
 		} else {
14
 		} else {
15
 			while (true) {
15
 			while (true) {

lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageUtils.java → lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageLoader.java View File

17
 import java.io.InputStream;
17
 import java.io.InputStream;
18
 import java.net.URL;
18
 import java.net.URL;
19
 
19
 
20
-public class ImageUtils {
20
+public class ImageLoader {
21
 
21
 
22
 	public interface ImageLoadingListener {
22
 	public interface ImageLoadingListener {
23
 		void onComplete(@NonNull Drawable drawable);
23
 		void onComplete(@NonNull Drawable drawable);
25
 		void onError(Throwable error);
25
 		void onError(Throwable error);
26
 	}
26
 	}
27
 
27
 
28
-	public static void loadIcon(final Context context, final String uri, final ImageLoadingListener listener) {
28
+	public void loadIcon(final Context context, final String uri, final ImageLoadingListener listener) {
29
         try {
29
         try {
30
             StrictMode.ThreadPolicy threadPolicy = adjustThreadPolicyDebug();
30
             StrictMode.ThreadPolicy threadPolicy = adjustThreadPolicyDebug();
31
             
31
             
40
         }
40
         }
41
     }
41
     }
42
 
42
 
43
-    private static StrictMode.ThreadPolicy adjustThreadPolicyDebug() {
43
+    private StrictMode.ThreadPolicy adjustThreadPolicyDebug() {
44
         StrictMode.ThreadPolicy threadPolicy = null;
44
         StrictMode.ThreadPolicy threadPolicy = null;
45
         if (NavigationApplication.instance.isDebug()) {
45
         if (NavigationApplication.instance.isDebug()) {
46
             threadPolicy = StrictMode.getThreadPolicy();
46
             threadPolicy = StrictMode.getThreadPolicy();
49
         return threadPolicy;
49
         return threadPolicy;
50
     }
50
     }
51
 
51
 
52
-    private static void restoreThreadPolicyDebug(@Nullable StrictMode.ThreadPolicy threadPolicy) {
52
+    private void restoreThreadPolicyDebug(@Nullable StrictMode.ThreadPolicy threadPolicy) {
53
         if (NavigationApplication.instance.isDebug() && threadPolicy != null) {
53
         if (NavigationApplication.instance.isDebug() && threadPolicy != null) {
54
             StrictMode.setThreadPolicy(threadPolicy);
54
             StrictMode.setThreadPolicy(threadPolicy);
55
         }
55
         }

+ 51
- 29
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/BottomTabsController.java View File

2
 
2
 
3
 import android.app.Activity;
3
 import android.app.Activity;
4
 import android.graphics.Color;
4
 import android.graphics.Color;
5
+import android.graphics.drawable.Drawable;
5
 import android.support.annotation.NonNull;
6
 import android.support.annotation.NonNull;
6
-import android.support.design.widget.BottomNavigationView;
7
-import android.view.Menu;
8
-import android.view.MenuItem;
9
 import android.view.View;
7
 import android.view.View;
10
 import android.view.ViewGroup;
8
 import android.view.ViewGroup;
11
 import android.widget.RelativeLayout;
9
 import android.widget.RelativeLayout;
12
 
10
 
11
+import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
12
+import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
13
+import com.reactnativenavigation.parse.BottomTabOptions;
14
+import com.reactnativenavigation.parse.BottomTabsOptions;
13
 import com.reactnativenavigation.parse.Options;
15
 import com.reactnativenavigation.parse.Options;
14
 import com.reactnativenavigation.parse.Text;
16
 import com.reactnativenavigation.parse.Text;
15
 import com.reactnativenavigation.presentation.NavigationOptionsListener;
17
 import com.reactnativenavigation.presentation.NavigationOptionsListener;
16
-import com.reactnativenavigation.utils.CompatUtils;
18
+import com.reactnativenavigation.utils.ImageLoader;
19
+import com.reactnativenavigation.utils.UiUtils;
20
+import com.reactnativenavigation.views.BottomTabs;
17
 
21
 
18
 import java.util.ArrayList;
22
 import java.util.ArrayList;
19
 import java.util.Collection;
23
 import java.util.Collection;
25
 import static android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM;
29
 import static android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM;
26
 import static com.reactnativenavigation.parse.DEFAULT_VALUES.NO_INT_VALUE;
30
 import static com.reactnativenavigation.parse.DEFAULT_VALUES.NO_INT_VALUE;
27
 
31
 
28
-public class BottomTabsController extends ParentController
29
-		implements BottomNavigationView.OnNavigationItemSelectedListener, NavigationOptionsListener {
30
-	private BottomNavigationView bottomNavigationView;
32
+public class BottomTabsController extends ParentController implements AHBottomNavigation.OnTabSelectedListener, NavigationOptionsListener {
33
+	private BottomTabs bottomTabs;
31
 	private List<ViewController> tabs = new ArrayList<>();
34
 	private List<ViewController> tabs = new ArrayList<>();
32
 	private int selectedIndex = 0;
35
 	private int selectedIndex = 0;
36
+    private ImageLoader imageLoader;
33
 
37
 
34
-	public BottomTabsController(final Activity activity, final String id) {
35
-		super(activity, id);
36
-	}
38
+    public BottomTabsController(final Activity activity, ImageLoader imageLoader, final String id, Options initialOptions) {
39
+		super(activity, id, initialOptions);
40
+        this.imageLoader = imageLoader;
41
+    }
37
 
42
 
38
 	@NonNull
43
 	@NonNull
39
 	@Override
44
 	@Override
40
 	protected ViewGroup createView() {
45
 	protected ViewGroup createView() {
41
 		RelativeLayout root = new RelativeLayout(getActivity());
46
 		RelativeLayout root = new RelativeLayout(getActivity());
42
-		bottomNavigationView = new BottomNavigationView(getActivity());
43
-		bottomNavigationView.setId(CompatUtils.generateViewId());
44
-		bottomNavigationView.setBackgroundColor(Color.DKGRAY);
45
-		bottomNavigationView.setOnNavigationItemSelectedListener(this);
47
+		bottomTabs = new BottomTabs(getActivity());
48
+        bottomTabs.setOnTabSelectedListener(this);
46
 		RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
49
 		RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
47
 		lp.addRule(ALIGN_PARENT_BOTTOM);
50
 		lp.addRule(ALIGN_PARENT_BOTTOM);
48
-		root.addView(bottomNavigationView, lp);
51
+		root.addView(bottomTabs, lp);
49
 		return root;
52
 		return root;
50
 	}
53
 	}
51
 
54
 
54
 		return !tabs.isEmpty() && tabs.get(selectedIndex).handleBack();
57
 		return !tabs.isEmpty() && tabs.get(selectedIndex).handleBack();
55
 	}
58
 	}
56
 
59
 
57
-	@Override
58
-	public boolean onNavigationItemSelected(@NonNull final MenuItem item) {
59
-		selectTabAtIndex(item.getItemId());
60
-		return true;
61
-	}
60
+    @Override
61
+    public boolean onTabSelected(int index, boolean wasSelected) {
62
+        selectTabAtIndex(index);
63
+        return true;
64
+    }
62
 
65
 
63
 	void selectTabAtIndex(final int newIndex) {
66
 	void selectTabAtIndex(final int newIndex) {
64
 		tabs.get(selectedIndex).getView().setVisibility(View.GONE);
67
 		tabs.get(selectedIndex).getView().setVisibility(View.GONE);
73
 		this.tabs = tabs;
76
 		this.tabs = tabs;
74
 		getView();
77
 		getView();
75
 		for (int i = 0; i < tabs.size(); i++) {
78
 		for (int i = 0; i < tabs.size(); i++) {
76
-			String title = String.valueOf(i);
77
-			createTab(tabs.get(i), i, title);
79
+			createTab(tabs.get(i), tabs.get(i).options.bottomTabOptions, tabs.get(i).options.bottomTabsOptions);
78
 		}
80
 		}
79
 		selectTabAtIndex(0);
81
 		selectTabAtIndex(0);
80
 	}
82
 	}
81
 
83
 
82
-	private void createTab(ViewController tab, final int index, final String title) {
83
-		bottomNavigationView.getMenu().add(0, index, Menu.NONE, title);
84
-		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
85
-		params.addRule(ABOVE, bottomNavigationView.getId());
86
-		tab.getView().setVisibility(View.GONE);
87
-		getView().addView(tab.getView(), params);
84
+	private void createTab(ViewController tab, final BottomTabOptions tabOptions, final BottomTabsOptions bottomTabsOptions) {
85
+	    if (!tabOptions.icon.hasValue()) {
86
+            throw new RuntimeException("BottomTab must have an icon");
87
+        }
88
+        imageLoader.loadIcon(getActivity(), tabOptions.icon.get(), new ImageLoader.ImageLoadingListener() {
89
+            @Override
90
+            public void onComplete(@NonNull Drawable drawable) {
91
+                setIconColor(drawable, bottomTabsOptions);
92
+                AHBottomNavigationItem item = new AHBottomNavigationItem(tabOptions.title.get(""), drawable);
93
+                bottomTabs.addItem(item);
94
+            }
95
+
96
+            @Override
97
+            public void onError(Throwable error) {
98
+                error.printStackTrace();
99
+            }
100
+        });
101
+
102
+        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
103
+        params.addRule(ABOVE, bottomTabs.getId());
104
+        tab.getView().setVisibility(View.GONE);
105
+        getView().addView(tab.getView(), params);
88
 	}
106
 	}
89
 
107
 
90
-	int getSelectedIndex() {
108
+    private void setIconColor(Drawable drawable, BottomTabsOptions options) {
109
+        UiUtils.tintDrawable(drawable, Color.RED);
110
+    }
111
+
112
+    int getSelectedIndex() {
91
 		return selectedIndex;
113
 		return selectedIndex;
92
 	}
114
 	}
93
 
115
 

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

18
                                    final String id,
18
                                    final String id,
19
                                    final String componentName,
19
                                    final String componentName,
20
                                    final ReactViewCreator viewCreator,
20
                                    final ReactViewCreator viewCreator,
21
-                                   final Options initialNavigationOptions) {
22
-        super(activity, id);
21
+                                   final Options initialOptions) {
22
+        super(activity, id, initialOptions);
23
         this.componentName = componentName;
23
         this.componentName = componentName;
24
         this.viewCreator = viewCreator;
24
         this.viewCreator = viewCreator;
25
-        options = initialNavigationOptions;
26
     }
25
     }
27
 
26
 
28
     @Override
27
     @Override

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

25
     private Options defaultOptions = new Options();
25
     private Options defaultOptions = new Options();
26
 
26
 
27
     public Navigator(final Activity activity) {
27
     public Navigator(final Activity activity) {
28
-		super(activity, "navigator" + CompatUtils.generateViewId());
28
+		super(activity, "navigator" + CompatUtils.generateViewId(), new Options());
29
 	}
29
 	}
30
 
30
 
31
     @NonNull
31
     @NonNull

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

13
 
13
 
14
 public abstract class ParentController<T extends ViewGroup> extends ViewController {
14
 public abstract class ParentController<T extends ViewGroup> extends ViewController {
15
 
15
 
16
-	public ParentController(final Activity activity, final String id) {
17
-		super(activity, id);
16
+	public ParentController(final Activity activity, final String id, Options initialOptions) {
17
+		super(activity, id, initialOptions);
18
 	}
18
 	}
19
 
19
 
20
 	@NonNull
20
 	@NonNull

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

7
 import android.view.View;
7
 import android.view.View;
8
 import android.view.ViewGroup;
8
 import android.view.ViewGroup;
9
 
9
 
10
+import com.reactnativenavigation.parse.Options;
11
+
10
 import java.util.ArrayList;
12
 import java.util.ArrayList;
11
 import java.util.Collection;
13
 import java.util.Collection;
12
 
14
 
19
 	private ViewController leftController;
21
 	private ViewController leftController;
20
 	private ViewController rightController;
22
 	private ViewController rightController;
21
 
23
 
22
-	public SideMenuController(final Activity activity, final String id) {
23
-		super(activity, id);
24
+	public SideMenuController(final Activity activity, final String id, Options initialOptions) {
25
+		super(activity, id, initialOptions);
24
 	}
26
 	}
25
 
27
 
26
 	@NonNull
28
 	@NonNull

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

24
     private final NavigationAnimator animator;
24
     private final NavigationAnimator animator;
25
     private StackLayout stackLayout;
25
     private StackLayout stackLayout;
26
 
26
 
27
-    public StackController(final Activity activity, String id) {
28
-		super(activity, id);
27
+    public StackController(final Activity activity, String id, Options initialOptions) {
28
+		super(activity, id, initialOptions);
29
         animator = new NavigationAnimator(activity);
29
         animator = new NavigationAnimator(activity);
30
     }
30
     }
31
 
31
 

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

39
     private boolean isDestroyed;
39
     private boolean isDestroyed;
40
     private ViewVisibilityListener viewVisibilityListener = new ViewVisibilityListenerAdapter();
40
     private ViewVisibilityListener viewVisibilityListener = new ViewVisibilityListenerAdapter();
41
 
41
 
42
-    public ViewController(Activity activity, String id) {
42
+    public ViewController(Activity activity, String id, Options initialOptions) {
43
         this.activity = activity;
43
         this.activity = activity;
44
         this.id = id;
44
         this.id = id;
45
+        options = initialOptions;
45
     }
46
     }
46
 
47
 
47
     protected abstract T createView();
48
     protected abstract T createView();

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

21
 
21
 
22
     private List<ViewController> tabs;
22
     private List<ViewController> tabs;
23
     private TopTabsLayoutCreator viewCreator;
23
     private TopTabsLayoutCreator viewCreator;
24
-    private Options options;
25
 
24
 
26
     public TopTabsController(Activity activity, String id, List<ViewController> tabs, TopTabsLayoutCreator viewCreator, Options options) {
25
     public TopTabsController(Activity activity, String id, List<ViewController> tabs, TopTabsLayoutCreator viewCreator, Options options) {
27
-        super(activity, id);
26
+        super(activity, id, options);
28
         this.viewCreator = viewCreator;
27
         this.viewCreator = viewCreator;
29
-        this.options = options;
30
         this.tabs = tabs;
28
         this.tabs = tabs;
31
         for (ViewController tab : tabs) {
29
         for (ViewController tab : tabs) {
32
             tab.setParentController(this);
30
             tab.setParentController(this);

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

1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+
5
+import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
6
+import com.reactnativenavigation.utils.CompatUtils;
7
+
8
+public class BottomTabs extends AHBottomNavigation {
9
+    public BottomTabs(Context context) {
10
+        super(context);
11
+        setId(CompatUtils.generateViewId());
12
+    }
13
+}

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

16
 
16
 
17
 import com.reactnativenavigation.parse.Button;
17
 import com.reactnativenavigation.parse.Button;
18
 import com.reactnativenavigation.parse.Options;
18
 import com.reactnativenavigation.parse.Options;
19
-import com.reactnativenavigation.utils.ImageUtils;
19
+import com.reactnativenavigation.utils.ImageLoader;
20
 import com.reactnativenavigation.utils.UiUtils;
20
 import com.reactnativenavigation.utils.UiUtils;
21
 
21
 
22
 import java.util.ArrayList;
22
 import java.util.ArrayList;
57
 			return;
57
 			return;
58
 		}
58
 		}
59
 
59
 
60
-		ImageUtils.loadIcon(context, button.icon.get(), new ImageUtils.ImageLoadingListener() {
60
+		new ImageLoader().loadIcon(context, button.icon.get(), new ImageLoader.ImageLoadingListener() {
61
 			@Override
61
 			@Override
62
 			public void onComplete(@NonNull Drawable drawable) {
62
 			public void onComplete(@NonNull Drawable drawable) {
63
 				icon = drawable;
63
 				icon = drawable;
74
 	}
74
 	}
75
 
75
 
76
 	private void applyIcon(Context context, final MenuItem menuItem) {
76
 	private void applyIcon(Context context, final MenuItem menuItem) {
77
-		ImageUtils.loadIcon(context, button.icon.get(), new ImageUtils.ImageLoadingListener() {
77
+        new ImageLoader().loadIcon(context, button.icon.get(), new ImageLoader.ImageLoadingListener() {
78
 			@Override
78
 			@Override
79
 			public void onComplete(@NonNull Drawable drawable) {
79
 			public void onComplete(@NonNull Drawable drawable) {
80
 				icon = drawable;
80
 				icon = drawable;

+ 49
- 0
lib/android/app/src/test/java/com/reactnativenavigation/mocks/ImageLoaderMock.java View File

1
+package com.reactnativenavigation.mocks;
2
+
3
+import android.graphics.Canvas;
4
+import android.graphics.ColorFilter;
5
+import android.graphics.drawable.Drawable;
6
+import android.support.annotation.NonNull;
7
+
8
+import com.reactnativenavigation.utils.ImageLoader;
9
+
10
+import org.mockito.Mockito;
11
+
12
+import static org.mockito.ArgumentMatchers.any;
13
+import static org.mockito.ArgumentMatchers.anyString;
14
+import static org.mockito.Mockito.doAnswer;
15
+
16
+public class ImageLoaderMock {
17
+    private static Drawable mockDrawable = new Drawable() {
18
+        @Override
19
+        public void draw(@NonNull Canvas canvas) {
20
+
21
+        }
22
+
23
+        @Override
24
+        public void setAlpha(int alpha) {
25
+
26
+        }
27
+
28
+        @Override
29
+        public void setColorFilter(@android.support.annotation.Nullable ColorFilter colorFilter) {
30
+
31
+        }
32
+
33
+        @Override
34
+        public int getOpacity() {
35
+            return 0;
36
+        }
37
+    };
38
+
39
+    public static ImageLoader mock() {
40
+        ImageLoader imageLoader = Mockito.mock(ImageLoader.class);
41
+        doAnswer(
42
+                invocation -> {
43
+                    ((ImageLoader.ImageLoadingListener) invocation.getArguments()[2]).onComplete(mockDrawable);
44
+                    return null;
45
+                }
46
+        ).when(imageLoader).loadIcon(any(), anyString(), any());
47
+        return imageLoader;
48
+    }
49
+}

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

15
 
15
 
16
 public class SimpleViewController extends ViewController<FrameLayout> {
16
 public class SimpleViewController extends ViewController<FrameLayout> {
17
 
17
 
18
-    public SimpleViewController(final Activity activity, String id) {
19
-        super(activity, id);
20
-        options = new Options();
18
+    public SimpleViewController(final Activity activity, String id, Options options) {
19
+        super(activity, id, options);
21
     }
20
     }
22
 
21
 
23
     @Override
22
     @Override

+ 10
- 12
lib/android/app/src/test/java/com/reactnativenavigation/parse/NavigationOptionsTest.java View File

27
     private static final Options.BooleanOptions TOP_BAR_HIDE_ON_SCROLL = True;
27
     private static final Options.BooleanOptions TOP_BAR_HIDE_ON_SCROLL = True;
28
     private static final Options.BooleanOptions BOTTOM_TABS_ANIMATE_HIDE = True;
28
     private static final Options.BooleanOptions BOTTOM_TABS_ANIMATE_HIDE = True;
29
     private static final Options.BooleanOptions BOTTOM_TABS_HIDDEN = True;
29
     private static final Options.BooleanOptions BOTTOM_TABS_HIDDEN = True;
30
-    private static final int BOTTOM_TABS_BADGE = 3;
30
+    private static final String BOTTOM_TABS_BADGE = "3";
31
     private static final String BOTTOM_TABS_CURRENT_TAB_ID = "ComponentId";
31
     private static final String BOTTOM_TABS_CURRENT_TAB_ID = "ComponentId";
32
     private static final int BOTTOM_TABS_CURRENT_TAB_INDEX = 1;
32
     private static final int BOTTOM_TABS_CURRENT_TAB_INDEX = 1;
33
     private TypefaceLoader mockLoader;
33
     private TypefaceLoader mockLoader;
47
     public void parsesJson() throws Exception {
47
     public void parsesJson() throws Exception {
48
         JSONObject json = new JSONObject()
48
         JSONObject json = new JSONObject()
49
                 .put("topBar", createTopBar())
49
                 .put("topBar", createTopBar())
50
-                .put("bottomTabs", createTabBar());
50
+                .put("bottomTabs", createBottomTabs());
51
         Options result = Options.parse(mockLoader, json);
51
         Options result = Options.parse(mockLoader, json);
52
         assertResult(result);
52
         assertResult(result);
53
     }
53
     }
62
         assertThat(result.topBarOptions.drawBehind).isEqualTo(TOP_BAR_DRAW_BEHIND);
62
         assertThat(result.topBarOptions.drawBehind).isEqualTo(TOP_BAR_DRAW_BEHIND);
63
         assertThat(result.topBarOptions.hideOnScroll).isEqualTo(TOP_BAR_HIDE_ON_SCROLL);
63
         assertThat(result.topBarOptions.hideOnScroll).isEqualTo(TOP_BAR_HIDE_ON_SCROLL);
64
         assertThat(result.bottomTabsOptions.animateHide).isEqualTo(BOTTOM_TABS_ANIMATE_HIDE);
64
         assertThat(result.bottomTabsOptions.animateHide).isEqualTo(BOTTOM_TABS_ANIMATE_HIDE);
65
-        assertThat(result.bottomTabsOptions.hidden).isEqualTo(BOTTOM_TABS_HIDDEN);
66
-        assertThat(result.bottomTabsOptions.tabBadge).isEqualTo(BOTTOM_TABS_BADGE);
65
+        assertThat(result.bottomTabsOptions.visible).isEqualTo(BOTTOM_TABS_HIDDEN);
67
         assertThat(result.bottomTabsOptions.currentTabId.get()).isEqualTo(BOTTOM_TABS_CURRENT_TAB_ID);
66
         assertThat(result.bottomTabsOptions.currentTabId.get()).isEqualTo(BOTTOM_TABS_CURRENT_TAB_ID);
68
         assertThat(result.bottomTabsOptions.currentTabIndex).isEqualTo(BOTTOM_TABS_CURRENT_TAB_INDEX);
67
         assertThat(result.bottomTabsOptions.currentTabIndex).isEqualTo(BOTTOM_TABS_CURRENT_TAB_INDEX);
69
     }
68
     }
70
 
69
 
71
     @NonNull
70
     @NonNull
72
-    private JSONObject createTabBar() throws JSONException {
71
+    private JSONObject createBottomTabs() throws JSONException {
73
         return new JSONObject()
72
         return new JSONObject()
74
                 .put("currentTabId", BOTTOM_TABS_CURRENT_TAB_ID)
73
                 .put("currentTabId", BOTTOM_TABS_CURRENT_TAB_ID)
75
                 .put("currentTabIndex", BOTTOM_TABS_CURRENT_TAB_INDEX)
74
                 .put("currentTabIndex", BOTTOM_TABS_CURRENT_TAB_INDEX)
76
-                .put("hidden", BOTTOM_TABS_HIDDEN)
77
-                .put("animateHide", BOTTOM_TABS_ANIMATE_HIDE)
78
-                .put("tabBadge", BOTTOM_TABS_BADGE);
75
+                .put("visible", BOTTOM_TABS_HIDDEN)
76
+                .put("animateHide", BOTTOM_TABS_ANIMATE_HIDE);
79
     }
77
     }
80
 
78
 
81
     @NonNull
79
     @NonNull
99
                 .put("textColor", TOP_BAR_TEXT_COLOR)
97
                 .put("textColor", TOP_BAR_TEXT_COLOR)
100
                 .put("textFontSize", TOP_BAR_FONT_SIZE)
98
                 .put("textFontSize", TOP_BAR_FONT_SIZE)
101
                 .put("textFontFamily", TOP_BAR_FONT_FAMILY)
99
                 .put("textFontFamily", TOP_BAR_FONT_FAMILY)
102
-                .put("hidden", TOP_BAR_HIDDEN);
100
+                .put("visible", TOP_BAR_HIDDEN);
103
     }
101
     }
104
 
102
 
105
     @NonNull
103
     @NonNull
107
         return new JSONObject()
105
         return new JSONObject()
108
                 .put("currentTabId", BOTTOM_TABS_CURRENT_TAB_ID)
106
                 .put("currentTabId", BOTTOM_TABS_CURRENT_TAB_ID)
109
                 .put("currentTabIndex", BOTTOM_TABS_CURRENT_TAB_INDEX)
107
                 .put("currentTabIndex", BOTTOM_TABS_CURRENT_TAB_INDEX)
110
-                .put("hidden", BOTTOM_TABS_HIDDEN)
108
+                .put("visible", BOTTOM_TABS_HIDDEN)
111
                 .put("animateHide", BOTTOM_TABS_ANIMATE_HIDE)
109
                 .put("animateHide", BOTTOM_TABS_ANIMATE_HIDE)
112
                 .put("tabBadge", BOTTOM_TABS_BADGE);
110
                 .put("tabBadge", BOTTOM_TABS_BADGE);
113
     }
111
     }
116
     public void mergeDefaultOptions() throws Exception {
114
     public void mergeDefaultOptions() throws Exception {
117
         JSONObject json = new JSONObject();
115
         JSONObject json = new JSONObject();
118
         json.put("topBar", createTopBar());
116
         json.put("topBar", createTopBar());
119
-        json.put("bottomTabs", createTabBar());
117
+        json.put("bottomTabs", createBottomTabs());
120
         Options defaultOptions = Options.parse(mockLoader, json);
118
         Options defaultOptions = Options.parse(mockLoader, json);
121
         Options options = new Options();
119
         Options options = new Options();
122
 
120
 
133
 
131
 
134
         JSONObject json = new JSONObject()
132
         JSONObject json = new JSONObject()
135
                 .put("topBar", createTopBar())
133
                 .put("topBar", createTopBar())
136
-                .put("bottomTabs", createTabBar());
134
+                .put("bottomTabs", createBottomTabs());
137
         Options options = Options.parse(mockLoader, json);
135
         Options options = Options.parse(mockLoader, json);
138
         options.withDefaultOptions(defaultOptions);
136
         options.withDefaultOptions(defaultOptions);
139
         assertResult(options);
137
         assertResult(options);

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

1
+package com.reactnativenavigation.utils;
2
+
3
+import com.reactnativenavigation.parse.Options;
4
+import com.reactnativenavigation.parse.Text;
5
+
6
+public class OptionHelper {
7
+    public static Options createBottomTabOptions() {
8
+        Options options = new Options();
9
+        options.bottomTabOptions.title = new Text("Tab");
10
+        options.bottomTabOptions.icon = new Text("http://127.0.0.1/icon.png");
11
+        return options;
12
+    }
13
+}

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

1
 package com.reactnativenavigation.viewcontrollers;
1
 package com.reactnativenavigation.viewcontrollers;
2
 
2
 
3
-import android.app.*;
4
-import android.support.annotation.*;
5
-import android.support.design.widget.*;
6
-import android.view.*;
7
-import android.widget.*;
8
-
9
-import com.reactnativenavigation.*;
10
-import com.reactnativenavigation.mocks.*;
11
-
12
-import org.assertj.core.api.iterable.*;
13
-import org.junit.*;
14
-
15
-import java.util.*;
16
-
17
-import static org.assertj.core.api.Java6Assertions.*;
18
-import static org.mockito.Mockito.*;
3
+import android.app.Activity;
4
+import android.support.annotation.NonNull;
5
+import android.view.View;
6
+import android.widget.RelativeLayout;
7
+
8
+import com.reactnativenavigation.BaseTest;
9
+import com.reactnativenavigation.mocks.ImageLoaderMock;
10
+import com.reactnativenavigation.mocks.MockPromise;
11
+import com.reactnativenavigation.mocks.SimpleViewController;
12
+import com.reactnativenavigation.parse.Options;
13
+import com.reactnativenavigation.utils.ImageLoader;
14
+import com.reactnativenavigation.utils.OptionHelper;
15
+import com.reactnativenavigation.views.BottomTabs;
16
+
17
+import org.assertj.core.api.iterable.Extractor;
18
+import org.junit.Test;
19
+
20
+import java.util.Arrays;
21
+import java.util.Collections;
22
+import java.util.List;
23
+
24
+import static org.assertj.core.api.Java6Assertions.assertThat;
25
+import static org.mockito.Mockito.spy;
26
+import static org.mockito.Mockito.times;
27
+import static org.mockito.Mockito.verify;
28
+import static org.mockito.Mockito.when;
19
 
29
 
20
 public class BottomTabsControllerTest extends BaseTest {
30
 public class BottomTabsControllerTest extends BaseTest {
21
 
31
 
26
     private ViewController child3;
36
     private ViewController child3;
27
     private ViewController child4;
37
     private ViewController child4;
28
     private ViewController child5;
38
     private ViewController child5;
39
+    private Options tabOptions = OptionHelper.createBottomTabOptions();
40
+    private ImageLoader imageLoaderMock = ImageLoaderMock.mock();
29
 
41
 
30
     @Override
42
     @Override
31
     public void beforeEach() {
43
     public void beforeEach() {
32
         super.beforeEach();
44
         super.beforeEach();
33
         activity = newActivity();
45
         activity = newActivity();
34
-        uut = new BottomTabsController(activity, "uut");
35
-        child1 = new SimpleViewController(activity, "child1");
36
-        child2 = new SimpleViewController(activity, "child2");
37
-        child3 = new SimpleViewController(activity, "child3");
38
-        child4 = new SimpleViewController(activity, "child4");
39
-        child5 = new SimpleViewController(activity, "child5");
46
+        uut = new BottomTabsController(activity, imageLoaderMock, "uut", new Options());
47
+        child1 = new SimpleViewController(activity, "child1", tabOptions);
48
+        child2 = new SimpleViewController(activity, "child2", tabOptions);
49
+        child3 = new SimpleViewController(activity, "child3", tabOptions);
50
+        child4 = new SimpleViewController(activity, "child4", tabOptions);
51
+        child5 = new SimpleViewController(activity, "child5", tabOptions);
40
     }
52
     }
41
 
53
 
42
     @Test
54
     @Test
43
     public void containsRelativeLayoutView() throws Exception {
55
     public void containsRelativeLayoutView() throws Exception {
44
         assertThat(uut.getView()).isInstanceOf(RelativeLayout.class);
56
         assertThat(uut.getView()).isInstanceOf(RelativeLayout.class);
45
-        assertThat(uut.getView().getChildAt(0)).isInstanceOf(BottomNavigationView.class);
57
+        assertThat(uut.getView().getChildAt(0)).isInstanceOf(BottomTabs.class);
46
     }
58
     }
47
 
59
 
48
     @Test(expected = RuntimeException.class)
60
     @Test(expected = RuntimeException.class)
49
     public void setTabs_ThrowWhenMoreThan5() throws Exception {
61
     public void setTabs_ThrowWhenMoreThan5() throws Exception {
50
         List<ViewController> tabs = createTabs();
62
         List<ViewController> tabs = createTabs();
51
-        tabs.add(new SimpleViewController(activity, "6"));
63
+        tabs.add(new SimpleViewController(activity, "6", tabOptions));
52
         uut.setTabs(tabs);
64
         uut.setTabs(tabs);
53
     }
65
     }
54
 
66
 
75
     public void findControllerById_ReturnsSelfOrChildren() throws Exception {
87
     public void findControllerById_ReturnsSelfOrChildren() throws Exception {
76
         assertThat(uut.findControllerById("123")).isNull();
88
         assertThat(uut.findControllerById("123")).isNull();
77
         assertThat(uut.findControllerById(uut.getId())).isEqualTo(uut);
89
         assertThat(uut.findControllerById(uut.getId())).isEqualTo(uut);
78
-        StackController inner = new StackController(activity, "inner");
90
+        StackController inner = new StackController(activity, "inner", tabOptions);
79
         inner.animatePush(child1, new MockPromise());
91
         inner.animatePush(child1, new MockPromise());
80
         assertThat(uut.findControllerById(child1.getId())).isNull();
92
         assertThat(uut.findControllerById(child1.getId())).isNull();
81
         uut.setTabs(Collections.singletonList(inner));
93
         uut.setTabs(Collections.singletonList(inner));

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

25
         super.beforeEach();
25
         super.beforeEach();
26
         Activity activity = newActivity();
26
         Activity activity = newActivity();
27
         view = spy(new TestComponentLayout(activity, new TestReactView(activity)));
27
         view = spy(new TestComponentLayout(activity, new TestReactView(activity)));
28
-        ParentController<StackLayout> parentController = new StackController(activity, "stack");
28
+        ParentController<StackLayout> parentController = new StackController(activity, "stack", new Options());
29
         uut = new ComponentViewController(activity, "componentId1", "componentName", (activity1, componentId, componentName) -> view, new Options());
29
         uut = new ComponentViewController(activity, "componentId1", "componentName", (activity1, componentId, componentName) -> view, new Options());
30
         uut.setParentController(parentController);
30
         uut.setParentController(parentController);
31
         parentController.ensureViewIsCreated();
31
         parentController.ensureViewIsCreated();

+ 31
- 20
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.app.*;
4
-import android.support.annotation.*;
3
+import android.app.Activity;
4
+import android.support.annotation.NonNull;
5
 
5
 
6
-import com.reactnativenavigation.*;
7
-import com.reactnativenavigation.mocks.*;
8
-import com.reactnativenavigation.parse.*;
9
-import com.reactnativenavigation.utils.*;
6
+import com.reactnativenavigation.BaseTest;
7
+import com.reactnativenavigation.mocks.ImageLoaderMock;
8
+import com.reactnativenavigation.mocks.MockPromise;
9
+import com.reactnativenavigation.mocks.SimpleComponentViewController;
10
+import com.reactnativenavigation.mocks.SimpleViewController;
11
+import com.reactnativenavigation.parse.Options;
12
+import com.reactnativenavigation.parse.Text;
13
+import com.reactnativenavigation.utils.CompatUtils;
14
+import com.reactnativenavigation.utils.ImageLoader;
15
+import com.reactnativenavigation.utils.OptionHelper;
10
 
16
 
11
-import org.junit.*;
17
+import org.junit.Test;
12
 
18
 
13
-import java.util.*;
19
+import java.util.Arrays;
14
 
20
 
15
 import javax.annotation.Nullable;
21
 import javax.annotation.Nullable;
16
 
22
 
17
-import static org.assertj.core.api.Java6Assertions.*;
18
-import static org.mockito.Mockito.*;
23
+import static org.assertj.core.api.Java6Assertions.assertThat;
24
+import static org.mockito.Mockito.spy;
25
+import static org.mockito.Mockito.times;
26
+import static org.mockito.Mockito.verify;
27
+import static org.mockito.Mockito.when;
19
 
28
 
20
 public class NavigatorTest extends BaseTest {
29
 public class NavigatorTest extends BaseTest {
21
     private Activity activity;
30
     private Activity activity;
26
     private ViewController child3;
35
     private ViewController child3;
27
     private ViewController child4;
36
     private ViewController child4;
28
     private ViewController child5;
37
     private ViewController child5;
29
-
38
+    private Options tabOptions = OptionHelper.createBottomTabOptions();
39
+    private ImageLoader imageLoaderMock;
30
 
40
 
31
     @Override
41
     @Override
32
     public void beforeEach() {
42
     public void beforeEach() {
33
         super.beforeEach();
43
         super.beforeEach();
44
+        imageLoaderMock = ImageLoaderMock.mock();
34
         activity = newActivity();
45
         activity = newActivity();
35
         uut = new Navigator(activity);
46
         uut = new Navigator(activity);
36
-        parentController = new StackController(activity, "stack");
47
+        parentController = new StackController(activity, "stack", new Options());
37
         parentController.ensureViewIsCreated();
48
         parentController.ensureViewIsCreated();
38
-        child1 = new SimpleViewController(activity, "child1");
39
-        child2 = new SimpleViewController(activity, "child2");
40
-        child3 = new SimpleViewController(activity, "child3");
41
-        child4 = new SimpleViewController(activity, "child4");
42
-        child5 = new SimpleViewController(activity, "child5");
49
+        child1 = new SimpleViewController(activity, "child1", tabOptions);
50
+        child2 = new SimpleViewController(activity, "child2", tabOptions);
51
+        child3 = new SimpleViewController(activity, "child3", tabOptions);
52
+        child4 = new SimpleViewController(activity, "child4", tabOptions);
53
+        child5 = new SimpleViewController(activity, "child5", tabOptions);
43
     }
54
     }
44
 
55
 
45
     @Test
56
     @Test
94
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
105
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
95
         uut.setRoot(bottomTabsController, new MockPromise());
106
         uut.setRoot(bottomTabsController, new MockPromise());
96
 
107
 
97
-        SimpleViewController newChild = new SimpleViewController(activity, "new child");
108
+        SimpleViewController newChild = new SimpleViewController(activity, "new child", tabOptions);
98
         uut.push(child2.getId(), newChild, new MockPromise());
109
         uut.push(child2.getId(), newChild, new MockPromise());
99
 
110
 
100
         assertThat(stack1.getChildControllers()).doesNotContain(newChild);
111
         assertThat(stack1.getChildControllers()).doesNotContain(newChild);
223
 
234
 
224
     @NonNull
235
     @NonNull
225
     private BottomTabsController newTabs() {
236
     private BottomTabsController newTabs() {
226
-        return new BottomTabsController(activity, "tabsController");
237
+        return new BottomTabsController(activity, imageLoaderMock, "tabsController", new Options());
227
     }
238
     }
228
 
239
 
229
     @NonNull
240
     @NonNull
230
     private StackController newStack() {
241
     private StackController newStack() {
231
-        return new StackController(activity, "stack" + CompatUtils.generateViewId());
242
+        return new StackController(activity, "stack" + CompatUtils.generateViewId(), tabOptions);
232
     }
243
     }
233
 
244
 
234
     @Test
245
     @Test

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

44
                 (activity1, componentId, componentName) -> view,
44
                 (activity1, componentId, componentName) -> view,
45
                 initialNavigationOptions
45
                 initialNavigationOptions
46
         );
46
         );
47
-        stackController = new StackController(activity, "stack");
47
+        stackController = new StackController(activity, "stack", new Options());
48
         stackController.ensureViewIsCreated();
48
         stackController.ensureViewIsCreated();
49
         uut.setParentController(stackController);
49
         uut.setParentController(stackController);
50
     }
50
     }
62
     public void initialOptionsAppliedOnAppear() throws Exception {
62
     public void initialOptionsAppliedOnAppear() throws Exception {
63
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
63
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
64
         initialNavigationOptions.topBarOptions.title = new Text("the title");
64
         initialNavigationOptions.topBarOptions.title = new Text("the title");
65
-        StackController stackController = new StackController(activity, "stackId");
65
+        StackController stackController = new StackController(activity, "stackId", new Options());
66
         stackController.animatePush(uut, new MockPromise() {});
66
         stackController.animatePush(uut, new MockPromise() {});
67
         assertThat(stackController.getTopBar().getTitle()).isEmpty();
67
         assertThat(stackController.getTopBar().getTitle()).isEmpty();
68
 
68
 

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

7
 
7
 
8
 import com.reactnativenavigation.*;
8
 import com.reactnativenavigation.*;
9
 import com.reactnativenavigation.mocks.*;
9
 import com.reactnativenavigation.mocks.*;
10
+import com.reactnativenavigation.parse.Options;
10
 
11
 
11
 import org.junit.*;
12
 import org.junit.*;
12
 
13
 
26
         super.beforeEach();
27
         super.beforeEach();
27
         activity = newActivity();
28
         activity = newActivity();
28
         children = new ArrayList<>();
29
         children = new ArrayList<>();
29
-        uut = new ParentController(activity, "uut") {
30
+        uut = new ParentController(activity, "uut", new Options()) {
30
 
31
 
31
             @NonNull
32
             @NonNull
32
             @Override
33
             @Override
54
 
55
 
55
     @Test
56
     @Test
56
     public void findControllerById_ChildById() throws Exception {
57
     public void findControllerById_ChildById() throws Exception {
57
-        SimpleViewController child1 = new SimpleViewController(activity, "child1");
58
-        SimpleViewController child2 = new SimpleViewController(activity, "child2");
58
+        SimpleViewController child1 = new SimpleViewController(activity, "child1", new Options());
59
+        SimpleViewController child2 = new SimpleViewController(activity, "child2", new Options());
59
         children.add(child1);
60
         children.add(child1);
60
         children.add(child2);
61
         children.add(child2);
61
 
62
 
65
 
66
 
66
     @Test
67
     @Test
67
     public void findControllerById_Recursive() throws Exception {
68
     public void findControllerById_Recursive() throws Exception {
68
-        StackController stackController = new StackController(activity, "stack");
69
-        SimpleViewController child1 = new SimpleViewController(activity, "child1");
70
-        SimpleViewController child2 = new SimpleViewController(activity, "child2");
69
+        StackController stackController = new StackController(activity, "stack", new Options());
70
+        SimpleViewController child1 = new SimpleViewController(activity, "child1", new Options());
71
+        SimpleViewController child2 = new SimpleViewController(activity, "child2", new Options());
71
         stackController.animatePush(child1, new MockPromise());
72
         stackController.animatePush(child1, new MockPromise());
72
         stackController.animatePush(child2, new MockPromise());
73
         stackController.animatePush(child2, new MockPromise());
73
         children.add(stackController);
74
         children.add(stackController);
77
 
78
 
78
     @Test
79
     @Test
79
     public void destroy_DestroysChildren() throws Exception {
80
     public void destroy_DestroysChildren() throws Exception {
80
-        ViewController child1 = spy(new SimpleViewController(activity, "child1"));
81
+        ViewController child1 = spy(new SimpleViewController(activity, "child1", new Options()));
81
         children.add(child1);
82
         children.add(child1);
82
 
83
 
83
         verify(child1, times(0)).destroy();
84
         verify(child1, times(0)).destroy();
87
 
88
 
88
     @Test
89
     @Test
89
     public void optionsAreClearedWhenChildIsAppeared() throws Exception {
90
     public void optionsAreClearedWhenChildIsAppeared() throws Exception {
90
-        StackController stackController = spy(new StackController(activity, "stack"));
91
-        SimpleViewController child1 = new SimpleViewController(activity, "child1");
91
+        StackController stackController = spy(new StackController(activity, "stack", new Options()));
92
+        SimpleViewController child1 = new SimpleViewController(activity, "child1", new Options());
92
         stackController.animatePush(child1, new MockPromise());
93
         stackController.animatePush(child1, new MockPromise());
93
 
94
 
94
         child1.onViewAppeared();
95
         child1.onViewAppeared();

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

5
 
5
 
6
 import com.reactnativenavigation.*;
6
 import com.reactnativenavigation.*;
7
 import com.reactnativenavigation.mocks.*;
7
 import com.reactnativenavigation.mocks.*;
8
+import com.reactnativenavigation.parse.Options;
8
 
9
 
9
 import org.assertj.core.api.iterable.*;
10
 import org.assertj.core.api.iterable.*;
10
 import org.junit.*;
11
 import org.junit.*;
26
     public void beforeEach() {
27
     public void beforeEach() {
27
         super.beforeEach();
28
         super.beforeEach();
28
         activity = newActivity();
29
         activity = newActivity();
29
-        uut = new StackController(activity, "uut");
30
-        child1 = new SimpleViewController(activity, "child1");
31
-        child2 = new SimpleViewController(activity, "child2");
32
-        child3 = new SimpleViewController(activity, "child3");
30
+        uut = new StackController(activity, "uut", new Options());
31
+        child1 = new SimpleViewController(activity, "child1", new Options());
32
+        child2 = new SimpleViewController(activity, "child2", new Options());
33
+        child3 = new SimpleViewController(activity, "child3", new Options());
33
     }
34
     }
34
 
35
 
35
     @Test
36
     @Test
84
         uut.animatePush(child1, new MockPromise());
85
         uut.animatePush(child1, new MockPromise());
85
         assertThat(child1.getParentController()).isEqualTo(uut);
86
         assertThat(child1.getParentController()).isEqualTo(uut);
86
 
87
 
87
-        StackController anotherNavController = new StackController(activity, "another");
88
+        StackController anotherNavController = new StackController(activity, "another", new Options());
88
         anotherNavController.animatePush(child2, new MockPromise());
89
         anotherNavController.animatePush(child2, new MockPromise());
89
         assertThat(child2.getParentController()).isEqualTo(anotherNavController);
90
         assertThat(child2.getParentController()).isEqualTo(anotherNavController);
90
     }
91
     }
264
 
265
 
265
     @Test
266
     @Test
266
     public void findControllerById_Deeply() throws Exception {
267
     public void findControllerById_Deeply() throws Exception {
267
-        StackController stack = new StackController(activity, "stack2");
268
+        StackController stack = new StackController(activity, "stack2", new Options());
268
         stack.animatePush(child2, new MockPromise());
269
         stack.animatePush(child2, new MockPromise());
269
         uut.animatePush(stack, new MockPromise());
270
         uut.animatePush(stack, new MockPromise());
270
         assertThat(uut.findControllerById(child2.getId())).isEqualTo(child2);
271
         assertThat(uut.findControllerById(child2.getId())).isEqualTo(child2);

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

1
-package com.reactnativenavigation.viewcontrollers;
2
-
3
-import android.app.*;
4
-import android.support.annotation.*;
5
-import android.view.*;
6
-
7
-import java.util.*;
8
-
9
-public class TopTabsControllerMock extends ParentController {
10
-    TopTabsControllerMock(Activity activity, String id) {
11
-        super(activity, id);
12
-    }
13
-
14
-    @NonNull
15
-    @Override
16
-    protected ViewGroup createView() {
17
-        return null;
18
-    }
19
-
20
-    @NonNull
21
-    @Override
22
-    public Collection<? extends ViewController> getChildControllers() {
23
-        return null;
24
-    }
25
-}

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

50
         uut = spy(new TopTabsController(activity, "componentId", tabControllers, layoutCreator, options));
50
         uut = spy(new TopTabsController(activity, "componentId", tabControllers, layoutCreator, options));
51
         tabControllers.forEach(viewController -> viewController.setParentController(uut));
51
         tabControllers.forEach(viewController -> viewController.setParentController(uut));
52
 
52
 
53
-        parentController = spy(new StackController(activity, "stackId"));
53
+        parentController = spy(new StackController(activity, "stackId", new Options()));
54
         uut.setParentController(parentController);
54
         uut.setParentController(parentController);
55
     }
55
     }
56
 
56
 

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

9
 import com.reactnativenavigation.BaseTest;
9
 import com.reactnativenavigation.BaseTest;
10
 import com.reactnativenavigation.mocks.MockPromise;
10
 import com.reactnativenavigation.mocks.MockPromise;
11
 import com.reactnativenavigation.mocks.SimpleViewController;
11
 import com.reactnativenavigation.mocks.SimpleViewController;
12
+import com.reactnativenavigation.parse.Options;
12
 
13
 
13
 import org.assertj.android.api.Assertions;
14
 import org.assertj.android.api.Assertions;
14
 import org.junit.Test;
15
 import org.junit.Test;
31
     public void beforeEach() {
32
     public void beforeEach() {
32
         super.beforeEach();
33
         super.beforeEach();
33
         activity = newActivity();
34
         activity = newActivity();
34
-        uut = new SimpleViewController(activity, "uut");
35
+        uut = new SimpleViewController(activity, "uut", new Options());
35
     }
36
     }
36
 
37
 
37
     @Test
38
     @Test
47
     @Test
48
     @Test
48
     public void canOverrideViewCreation() throws Exception {
49
     public void canOverrideViewCreation() throws Exception {
49
         final FrameLayout otherView = new FrameLayout(activity);
50
         final FrameLayout otherView = new FrameLayout(activity);
50
-        ViewController myController = new ViewController(activity, "vc") {
51
+        ViewController myController = new ViewController(activity, "vc", new Options()) {
51
             @Override
52
             @Override
52
             protected FrameLayout createView() {
53
             protected FrameLayout createView() {
53
                 return otherView;
54
                 return otherView;
59
     @Test
60
     @Test
60
     public void holdsAReferenceToStackControllerOrNull() throws Exception {
61
     public void holdsAReferenceToStackControllerOrNull() throws Exception {
61
         assertThat(uut.getParentController()).isNull();
62
         assertThat(uut.getParentController()).isNull();
62
-        StackController nav = new StackController(activity, "stack");
63
+        StackController nav = new StackController(activity, "stack", new Options());
63
         nav.animatePush(uut, new MockPromise());
64
         nav.animatePush(uut, new MockPromise());
64
         assertThat(uut.getParentController()).isEqualTo(nav);
65
         assertThat(uut.getParentController()).isEqualTo(nav);
65
     }
66
     }
143
 
144
 
144
     @Test
145
     @Test
145
     public void onDestroy_RemovesGlobalLayoutListener() throws Exception {
146
     public void onDestroy_RemovesGlobalLayoutListener() throws Exception {
146
-        new SimpleViewController(activity, "ensureNotNull").destroy();
147
+        new SimpleViewController(activity, "ensureNotNull", new Options()).destroy();
147
 
148
 
148
         ViewController spy = spy(uut);
149
         ViewController spy = spy(uut);
149
         View view = spy.getView();
150
         View view = spy.getView();

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

13
 @property (nonatomic, strong) NSNumber* hideShadow;
13
 @property (nonatomic, strong) NSNumber* hideShadow;
14
 @property (nonatomic, strong) NSNumber* backgroundColor;
14
 @property (nonatomic, strong) NSNumber* backgroundColor;
15
 @property (nonatomic, strong) NSNumber* textColor;
15
 @property (nonatomic, strong) NSNumber* textColor;
16
+@property (nonatomic, strong) NSNumber* tabColor;
17
+@property (nonatomic, strong) NSNumber* selectedTabColor;
16
 @property (nonatomic, strong) NSNumber* selectedTextColor;
18
 @property (nonatomic, strong) NSNumber* selectedTextColor;
17
 @property (nonatomic, strong) NSString* fontFamily;
19
 @property (nonatomic, strong) NSString* fontFamily;
18
 @property (nonatomic, strong) NSNumber* fontSize;
20
 @property (nonatomic, strong) NSNumber* fontSize;

+ 4
- 1
lib/ios/RNNControllerFactory.m View File

97
 
97
 
98
 - (UIViewController<RNNRootViewProtocol> *)createStack:(RNNLayoutNode*)node {
98
 - (UIViewController<RNNRootViewProtocol> *)createStack:(RNNLayoutNode*)node {
99
 	RNNNavigationController* vc = [[RNNNavigationController alloc] init];
99
 	RNNNavigationController* vc = [[RNNNavigationController alloc] init];
100
-	
100
+	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:node.data[@"options"]];
101
 	NSMutableArray* controllers = [NSMutableArray new];
101
 	NSMutableArray* controllers = [NSMutableArray new];
102
 	for (NSDictionary* child in node.children) {
102
 	for (NSDictionary* child in node.children) {
103
 		[controllers addObject:[self fromTree:child]];
103
 		[controllers addObject:[self fromTree:child]];
104
 	}
104
 	}
105
 	[vc setViewControllers:controllers];
105
 	[vc setViewControllers:controllers];
106
+	[vc setOptions:options];
106
 	
107
 	
107
 	return vc;
108
 	return vc;
108
 }
109
 }
109
 
110
 
110
 -(UIViewController<RNNRootViewProtocol> *)createTabs:(RNNLayoutNode*)node {
111
 -(UIViewController<RNNRootViewProtocol> *)createTabs:(RNNLayoutNode*)node {
111
 	RNNTabBarController* vc = [[RNNTabBarController alloc] init];
112
 	RNNTabBarController* vc = [[RNNTabBarController alloc] init];
113
+	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:node.data[@"options"]];
112
 	
114
 	
113
 	NSMutableArray* controllers = [NSMutableArray new];
115
 	NSMutableArray* controllers = [NSMutableArray new];
114
 	for (NSDictionary *child in node.children) {
116
 	for (NSDictionary *child in node.children) {
119
 		[controllers addObject:childVc];
121
 		[controllers addObject:childVc];
120
 	}
122
 	}
121
 	[vc setViewControllers:controllers];
123
 	[vc setViewControllers:controllers];
124
+	[vc setOptions:options];
122
 	
125
 	
123
 	return vc;
126
 	return vc;
124
 }
127
 }

+ 4
- 0
lib/ios/RNNNavigationController.m View File

16
 	return rootVC.isAnimated;
16
 	return rootVC.isAnimated;
17
 }
17
 }
18
 
18
 
19
+- (void)setOptions:(RNNNavigationOptions *)options {
20
+	((UIViewController<RNNRootViewProtocol>*)self.topViewController).options = options;
21
+}
22
+
19
 - (NSString *)componentId {
23
 - (NSString *)componentId {
20
 	return ((UIViewController<RNNRootViewProtocol>*)self.topViewController).componentId;
24
 	return ((UIViewController<RNNRootViewProtocol>*)self.topViewController).componentId;
21
 }
25
 }

+ 3
- 0
lib/ios/RNNRootViewProtocol.h View File

2
 
2
 
3
 @protocol RNNRootViewProtocol <NSObject, UINavigationControllerDelegate>
3
 @protocol RNNRootViewProtocol <NSObject, UINavigationControllerDelegate>
4
 
4
 
5
+@optional
6
+- (void)setOptions:(RNNNavigationOptions*)options;
7
+
5
 @required
8
 @required
6
 
9
 
7
 - (BOOL)isCustomTransitioned;
10
 - (BOOL)isCustomTransitioned;

+ 4
- 0
lib/ios/RNNTabBarController.m View File

41
 	return YES;
41
 	return YES;
42
 }
42
 }
43
 
43
 
44
+- (void)setOptions:(RNNNavigationOptions *)options {
45
+	[((UIViewController<RNNRootViewProtocol>*)self.selectedViewController) setOptions:options];
46
+}
47
+
44
 - (NSString *)componentId {
48
 - (NSString *)componentId {
45
 	return ((UIViewController<RNNRootViewProtocol>*)self.selectedViewController).componentId;
49
 	return ((UIViewController<RNNRootViewProtocol>*)self.selectedViewController).componentId;
46
 }
50
 }

+ 7
- 2
lib/src/commands/LayoutTreeCrawler.ts View File

2
 import { OptionsProcessor } from './OptionsProcessor';
2
 import { OptionsProcessor } from './OptionsProcessor';
3
 import { LayoutType, isLayoutType } from './LayoutType';
3
 import { LayoutType, isLayoutType } from './LayoutType';
4
 
4
 
5
+export interface Data {
6
+  name?: string;
7
+  options?: any;
8
+  passProps?: any;
9
+}
5
 export interface LayoutNode {
10
 export interface LayoutNode {
6
   id?: string;
11
   id?: string;
7
   type: LayoutType;
12
   type: LayoutType;
8
-  data: object;
13
+  data: Data;
9
   children: LayoutNode[];
14
   children: LayoutNode[];
10
 }
15
 }
11
 
16
 
24
     if (node.type === LayoutType.Component) {
29
     if (node.type === LayoutType.Component) {
25
       this._handleComponent(node);
30
       this._handleComponent(node);
26
     }
31
     }
32
+    OptionsProcessor.processOptions(node.data.options);
27
     _.forEach(node.children, this.crawl);
33
     _.forEach(node.children, this.crawl);
28
   }
34
   }
29
 
35
 
31
     this._assertComponentDataName(node);
37
     this._assertComponentDataName(node);
32
     this._savePropsToStore(node);
38
     this._savePropsToStore(node);
33
     this._applyStaticOptions(node);
39
     this._applyStaticOptions(node);
34
-    OptionsProcessor.processOptions(node.data.options);
35
   }
40
   }
36
 
41
 
37
   _savePropsToStore(node) {
42
   _savePropsToStore(node) {

BIN
playground/src/images/one@2x.png View File


BIN
playground/src/images/one_selected@2x.png View File


BIN
playground/src/images/three@2x.png View File


BIN
playground/src/images/three_selected@2x.png View File


BIN
playground/src/images/two@2x.png View File


BIN
playground/src/images/two_selected@2x.png View File


+ 1
- 1
playground/src/screens/StaticLifecycleOverlay.js View File

1
 const React = require('react');
1
 const React = require('react');
2
 const { Component } = require('react');
2
 const { Component } = require('react');
3
 const { View, Text } = require('react-native');
3
 const { View, Text } = require('react-native');
4
-const Navigation = require('react-native-navigation');
4
+const { Navigation } = require('react-native-navigation');
5
 
5
 
6
 class StaticLifecycleOverlay extends Component {
6
 class StaticLifecycleOverlay extends Component {
7
   constructor(props) {
7
   constructor(props) {

+ 59
- 25
playground/src/screens/WelcomeScreen.js View File

59
                     passProps: {
59
                     passProps: {
60
                       text: 'This is tab 1',
60
                       text: 'This is tab 1',
61
                       myFunction: () => 'Hello from a function!'
61
                       myFunction: () => 'Hello from a function!'
62
-                    },
63
-                    options: {
64
-                      bottomTab: {
65
-                        title: 'Tab 1',
66
-                        testID: testIDs.FIRST_TAB_BAR_BUTTON
67
-                      },
68
-                      bottomTabs: {
69
-                        textColor: '#12766b',
70
-                        selectedTextColor: 'red',
71
-                        fontFamily: 'HelveticaNeue-Italic',
72
-                        fontSize: 13
73
-                      }
74
                     }
62
                     }
75
                   }
63
                   }
76
                 }
64
                 }
77
-              ]
65
+              ],
66
+              options: {
67
+                bottomTab: {
68
+                  title: 'Tab 1',
69
+                  icon: require('../images/one.png'),
70
+                  testID: testIDs.FIRST_TAB_BAR_BUTTON
71
+                }
72
+              }
78
             }
73
             }
79
           },
74
           },
80
           {
75
           {
85
                     name: 'navigation.playground.TextScreen',
80
                     name: 'navigation.playground.TextScreen',
86
                     passProps: {
81
                     passProps: {
87
                       text: 'This is tab 2'
82
                       text: 'This is tab 2'
88
-                    },
89
-                    options: {
90
-                      bottomTab: {
91
-                        title: 'Tab 2',
92
-                        testID: testIDs.SECOND_TAB_BAR_BUTTON
93
-                      }
94
                     }
83
                     }
95
                   }
84
                   }
96
                 }
85
                 }
97
-              ]
86
+              ],
87
+              options: {
88
+                bottomTab: {
89
+                  title: 'Tab 2',
90
+                  icon: require('../images/two.png'),
91
+                  testID: testIDs.SECOND_TAB_BAR_BUTTON
92
+                }
93
+              }
98
             }
94
             }
99
           }
95
           }
100
-        ]
96
+        ],
97
+        options: {
98
+          bottomTabs: {
99
+            tabColor: 'red',
100
+            selectedTabColor: 'blue',
101
+            fontFamily: 'HelveticaNeue-Italic',
102
+            fontSize: 13,
103
+            testID: testIDs.BOTTOM_TABS_ELEMENT
104
+          }
105
+        }
101
       }
106
       }
102
     });
107
     });
103
   }
108
   }
127
                         }
132
                         }
128
                       }
133
                       }
129
                     }
134
                     }
130
-                  ]
135
+                  ],
136
+                  options: {
137
+                    bottomTab: {
138
+                      title: 'Tab 1',
139
+                      icon: require('../images/one.png'),
140
+                      testID: testIDs.FIRST_TAB_BAR_BUTTON
141
+                    }
142
+                  }
131
                 }
143
                 }
132
               },
144
               },
133
               {
145
               {
141
                         }
153
                         }
142
                       }
154
                       }
143
                     }
155
                     }
144
-                  ]
156
+                  ],
157
+                  options: {
158
+                    bottomTab: {
159
+                      title: 'Tab 2',
160
+                      icon: require('../images/two.png'),
161
+                      testID: testIDs.SECOND_TAB_BAR_BUTTON
162
+                    }
163
+                  }
145
                 }
164
                 }
146
               },
165
               },
147
               {
166
               {
155
                         }
174
                         }
156
                       }
175
                       }
157
                     }
176
                     }
158
-                  ]
177
+                  ],
178
+                  options: {
179
+                    bottomTab: {
180
+                      title: 'Tab 3',
181
+                      icon: require('../images/three.png'),
182
+                      testID: testIDs.SECOND_TAB_BAR_BUTTON
183
+                    }
184
+                  }
159
                 }
185
                 }
160
               }
186
               }
161
-            ]
187
+            ],
188
+            options: {
189
+              bottomTabs: {
190
+                tabColor: 'red',
191
+                selectedTabColor: 'blue',
192
+                fontFamily: 'HelveticaNeue-Italic',
193
+                fontSize: 13
194
+              }
195
+            }
162
           }
196
           }
163
         },
197
         },
164
         right: {
198
         right: {