Browse Source

Android implementation of Navigation.setDefaultOptions (#2299)

Usage
```js
Navigation.setDefaultOptions({
      topBar: {
        backgroundColor: 'red'
      }
    });
```
Guy Carmeli 7 years ago
parent
commit
cdbb834c82

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

1
 package com.reactnativenavigation.parse;
1
 package com.reactnativenavigation.parse;
2
 
2
 
3
 
3
 
4
+import com.reactnativenavigation.parse.NavigationOptions.BooleanOptions;
5
+
4
 import org.json.JSONObject;
6
 import org.json.JSONObject;
5
 
7
 
6
 public class BottomTabsOptions implements DEFAULT_VALUES {
8
 public class BottomTabsOptions implements DEFAULT_VALUES {
12
 		options.currentTabId = json.optString("currentTabId", NO_VALUE);
14
 		options.currentTabId = json.optString("currentTabId", NO_VALUE);
13
 		options.currentTabIndex = json.optInt("currentTabIndex", NO_INT_VALUE);
15
 		options.currentTabIndex = json.optInt("currentTabIndex", NO_INT_VALUE);
14
 		options.tabBadge = json.optInt("tabBadge", NO_INT_VALUE);
16
 		options.tabBadge = json.optInt("tabBadge", NO_INT_VALUE);
15
-		options.hidden = NavigationOptions.BooleanOptions.parse(json.optString("hidden"));
16
-		options.animateHide = NavigationOptions.BooleanOptions.parse(json.optString("animateHide"));
17
+		options.hidden = BooleanOptions.parse(json.optString("hidden"));
18
+		options.animateHide = BooleanOptions.parse(json.optString("animateHide"));
17
 
19
 
18
 		return options;
20
 		return options;
19
 	}
21
 	}
20
 
22
 
21
 	public int tabBadge = NO_INT_VALUE;
23
 	public int tabBadge = NO_INT_VALUE;
22
-	public NavigationOptions.BooleanOptions hidden = NavigationOptions.BooleanOptions.False;
23
-	public NavigationOptions.BooleanOptions animateHide = NavigationOptions.BooleanOptions.False;
24
+	public BooleanOptions hidden = BooleanOptions.False;
25
+	public BooleanOptions animateHide = BooleanOptions.False;
24
 	public int currentTabIndex = NO_INT_VALUE;
26
 	public int currentTabIndex = NO_INT_VALUE;
25
 	public String currentTabId = NO_VALUE;
27
 	public String currentTabId = NO_VALUE;
26
 
28
 
29
 			currentTabId = other.currentTabId;
31
 			currentTabId = other.currentTabId;
30
 		}
32
 		}
31
 		if (NO_INT_VALUE != other.currentTabIndex) {
33
 		if (NO_INT_VALUE != other.currentTabIndex) {
32
-			currentTabId = other.currentTabId;
34
+            currentTabIndex = other.currentTabIndex;
33
 		}
35
 		}
34
 		if (NO_INT_VALUE != other.tabBadge) {
36
 		if (NO_INT_VALUE != other.tabBadge) {
35
 			tabBadge = other.tabBadge;
37
 			tabBadge = other.tabBadge;
36
 		}
38
 		}
37
-		if (other.hidden != NavigationOptions.BooleanOptions.NoValue) {
39
+		if (other.hidden != BooleanOptions.NoValue) {
38
 			hidden = other.hidden;
40
 			hidden = other.hidden;
39
 		}
41
 		}
40
-		if (other.animateHide != NavigationOptions.BooleanOptions.NoValue) {
42
+		if (other.animateHide != BooleanOptions.NoValue) {
41
 			animateHide = other.animateHide;
43
 			animateHide = other.animateHide;
42
 		}
44
 		}
43
 	}
45
 	}
46
+
47
+    void mergeWithDefault(final BottomTabsOptions defaultOptions) {
48
+        if (NO_VALUE.equals(currentTabId)) {
49
+            currentTabId = defaultOptions.currentTabId;
50
+        }
51
+        if (NO_INT_VALUE == currentTabIndex) {
52
+            currentTabIndex = defaultOptions.currentTabIndex;
53
+        }
54
+        if (NO_INT_VALUE == tabBadge) {
55
+            tabBadge = defaultOptions.tabBadge;
56
+        }
57
+        if (hidden == BooleanOptions.NoValue) {
58
+            hidden = defaultOptions.hidden;
59
+        }
60
+        if (animateHide == BooleanOptions.NoValue) {
61
+            animateHide = defaultOptions.animateHide;
62
+        }
63
+    }
44
 }
64
 }

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

19
 
19
 
20
 	private final Activity activity;
20
 	private final Activity activity;
21
 	private final ReactInstanceManager reactInstanceManager;
21
 	private final ReactInstanceManager reactInstanceManager;
22
+    private NavigationOptions defaultOptions;
22
 
23
 
23
-	public LayoutFactory(Activity activity, final ReactInstanceManager reactInstanceManager) {
24
+    public LayoutFactory(Activity activity, final ReactInstanceManager reactInstanceManager, NavigationOptions defaultOptions) {
24
 		this.activity = activity;
25
 		this.activity = activity;
25
 		this.reactInstanceManager = reactInstanceManager;
26
 		this.reactInstanceManager = reactInstanceManager;
26
-	}
27
+        this.defaultOptions = defaultOptions;
28
+    }
27
 
29
 
28
 	public ViewController create(final LayoutNode node) {
30
 	public ViewController create(final LayoutNode node) {
29
 		switch (node.type) {
31
 		switch (node.type) {
84
 	private ViewController createContainer(LayoutNode node) {
86
 	private ViewController createContainer(LayoutNode node) {
85
 		String id = node.id;
87
 		String id = node.id;
86
 		String name = node.data.optString("name");
88
 		String name = node.data.optString("name");
87
-		NavigationOptions navigationOptions = NavigationOptions.parse(node.data.optJSONObject("navigationOptions"));
89
+		NavigationOptions navigationOptions = NavigationOptions.parse(node.data.optJSONObject("navigationOptions"), defaultOptions);
88
 		return new ContainerViewController(activity, id, name,
90
 		return new ContainerViewController(activity, id, name,
89
 				new TopbarContainerViewCreator(new ReactContainerViewCreator(reactInstanceManager)), navigationOptions);
91
 				new TopbarContainerViewCreator(new ReactContainerViewCreator(reactInstanceManager)), navigationOptions);
90
 	}
92
 	}

+ 15
- 6
lib/android/app/src/main/java/com/reactnativenavigation/parse/NavigationOptions.java View File

1
 package com.reactnativenavigation.parse;
1
 package com.reactnativenavigation.parse;
2
 
2
 
3
-import android.graphics.Color;
4
-import android.support.annotation.ColorInt;
5
 import android.support.annotation.NonNull;
3
 import android.support.annotation.NonNull;
6
 
4
 
7
 import org.json.JSONObject;
5
 import org.json.JSONObject;
8
 
6
 
9
 public class NavigationOptions implements DEFAULT_VALUES {
7
 public class NavigationOptions implements DEFAULT_VALUES {
10
 
8
 
11
-	public enum BooleanOptions {
9
+    public enum BooleanOptions {
12
 		True,
10
 		True,
13
 		False,
11
 		False,
14
 		NoValue;
12
 		NoValue;
21
 		}
19
 		}
22
 	}
20
 	}
23
 
21
 
22
+    @NonNull
23
+    public static NavigationOptions parse(JSONObject json) {
24
+        return parse(json, new NavigationOptions());
25
+    }
26
+
24
 	@NonNull
27
 	@NonNull
25
-	public static NavigationOptions parse(JSONObject json) {
28
+	public static NavigationOptions parse(JSONObject json, @NonNull NavigationOptions defaultOptions) {
26
 		NavigationOptions result = new NavigationOptions();
29
 		NavigationOptions result = new NavigationOptions();
27
 		if (json == null) return result;
30
 		if (json == null) return result;
28
 
31
 
29
 		result.topBarOptions = TopBarOptions.parse(json.optJSONObject("topBar"));
32
 		result.topBarOptions = TopBarOptions.parse(json.optJSONObject("topBar"));
30
 		result.bottomTabsOptions = BottomTabsOptions.parse(json.optJSONObject("tabBar"));
33
 		result.bottomTabsOptions = BottomTabsOptions.parse(json.optJSONObject("tabBar"));
31
 
34
 
32
-		return result;
35
+		return result.withDefaultOptions(defaultOptions);
33
 	}
36
 	}
34
 
37
 
35
 	public TopBarOptions topBarOptions = new TopBarOptions();
38
 	public TopBarOptions topBarOptions = new TopBarOptions();
36
 	public BottomTabsOptions bottomTabsOptions = new BottomTabsOptions();
39
 	public BottomTabsOptions bottomTabsOptions = new BottomTabsOptions();
37
 
40
 
38
 	public void mergeWith(final NavigationOptions other) {
41
 	public void mergeWith(final NavigationOptions other) {
39
-		topBarOptions.mergeWith(other.topBarOptions);
42
+        topBarOptions.mergeWith(other.topBarOptions);
40
 		bottomTabsOptions.mergeWith(other.bottomTabsOptions);
43
 		bottomTabsOptions.mergeWith(other.bottomTabsOptions);
41
 	}
44
 	}
45
+
46
+    NavigationOptions withDefaultOptions(final NavigationOptions other) {
47
+        topBarOptions.mergeWithDefault(other.topBarOptions);
48
+        bottomTabsOptions.mergeWithDefault(other.bottomTabsOptions);
49
+        return this;
50
+    }
42
 }
51
 }

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

49
 			animateHide = other.animateHide;
49
 			animateHide = other.animateHide;
50
 		}
50
 		}
51
 	}
51
 	}
52
+
53
+    void mergeWithDefault(TopBarOptions defaultOptions) {
54
+        if (NO_VALUE.equals(title))
55
+            title = defaultOptions.title;
56
+        if (backgroundColor == NO_COLOR_VALUE)
57
+            backgroundColor = defaultOptions.backgroundColor;
58
+        if (textColor == NO_COLOR_VALUE)
59
+            textColor = defaultOptions.textColor;
60
+        if (textFontSize == NO_FLOAT_VALUE)
61
+            textFontSize = defaultOptions.textFontSize;
62
+        if (NO_VALUE.equals(textFontFamily))
63
+            textFontFamily = defaultOptions.textFontFamily;
64
+        if (hidden == NavigationOptions.BooleanOptions.NoValue)
65
+            hidden = defaultOptions.hidden;
66
+        if (animateHide == NavigationOptions.BooleanOptions.NoValue)
67
+            animateHide = defaultOptions.animateHide;
68
+    }
52
 }
69
 }

+ 14
- 6
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java View File

9
 import com.facebook.react.bridge.ReactMethod;
9
 import com.facebook.react.bridge.ReactMethod;
10
 import com.facebook.react.bridge.ReadableMap;
10
 import com.facebook.react.bridge.ReadableMap;
11
 import com.reactnativenavigation.NavigationActivity;
11
 import com.reactnativenavigation.NavigationActivity;
12
+import com.reactnativenavigation.parse.JSONParser;
12
 import com.reactnativenavigation.parse.LayoutFactory;
13
 import com.reactnativenavigation.parse.LayoutFactory;
13
 import com.reactnativenavigation.parse.LayoutNode;
14
 import com.reactnativenavigation.parse.LayoutNode;
14
-import com.reactnativenavigation.parse.NavigationOptions;
15
-import com.reactnativenavigation.parse.JSONParser;
16
 import com.reactnativenavigation.parse.LayoutNodeParser;
15
 import com.reactnativenavigation.parse.LayoutNodeParser;
16
+import com.reactnativenavigation.parse.NavigationOptions;
17
 import com.reactnativenavigation.parse.OverlayOptions;
17
 import com.reactnativenavigation.parse.OverlayOptions;
18
 import com.reactnativenavigation.utils.UiThread;
18
 import com.reactnativenavigation.utils.UiThread;
19
-import com.reactnativenavigation.viewcontrollers.ContainerViewController;
20
 import com.reactnativenavigation.viewcontrollers.Navigator;
19
 import com.reactnativenavigation.viewcontrollers.Navigator;
21
 import com.reactnativenavigation.viewcontrollers.ViewController;
20
 import com.reactnativenavigation.viewcontrollers.ViewController;
22
 import com.reactnativenavigation.viewcontrollers.overlay.OverlayFactory;
21
 import com.reactnativenavigation.viewcontrollers.overlay.OverlayFactory;
23
 
22
 
24
-import org.json.JSONObject;
25
-
26
 public class NavigationModule extends ReactContextBaseJavaModule {
23
 public class NavigationModule extends ReactContextBaseJavaModule {
27
 	private static final String NAME = "RNNBridgeModule";
24
 	private static final String NAME = "RNNBridgeModule";
28
 	private final ReactInstanceManager reactInstanceManager;
25
 	private final ReactInstanceManager reactInstanceManager;
49
 		});
46
 		});
50
 	}
47
 	}
51
 
48
 
49
+	@ReactMethod
50
+	public void setDefaultOptions(final ReadableMap options) {
51
+        final NavigationOptions defaultOptions = NavigationOptions.parse(JSONParser.parse(options));
52
+        handle(new Runnable() {
53
+            @Override
54
+            public void run() {
55
+                navigator().setDefaultOptions(defaultOptions);
56
+            }
57
+        });
58
+    }
59
+
52
 	@ReactMethod
60
 	@ReactMethod
53
 	public void setOptions(final String onContainerId, final ReadableMap options) {
61
 	public void setOptions(final String onContainerId, final ReadableMap options) {
54
 		final NavigationOptions navOptions = NavigationOptions.parse(JSONParser.parse(options));
62
 		final NavigationOptions navOptions = NavigationOptions.parse(JSONParser.parse(options));
178
 
186
 
179
 	@NonNull
187
 	@NonNull
180
 	private LayoutFactory newLayoutFactory() {
188
 	private LayoutFactory newLayoutFactory() {
181
-		return new LayoutFactory(activity(), reactInstanceManager);
189
+		return new LayoutFactory(activity(), reactInstanceManager, navigator().getDefaultOptions());
182
 	}
190
 	}
183
 
191
 
184
 	private void handle(Runnable task) {
192
 	private void handle(Runnable task) {

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

12
 import com.reactnativenavigation.presentation.OverlayPresenter;
12
 import com.reactnativenavigation.presentation.OverlayPresenter;
13
 import com.reactnativenavigation.utils.CompatUtils;
13
 import com.reactnativenavigation.utils.CompatUtils;
14
 
14
 
15
-import org.json.JSONObject;
16
-
17
 import java.util.Collection;
15
 import java.util.Collection;
18
 import java.util.Collections;
16
 import java.util.Collections;
19
 
17
 
22
 	private final ModalStack modalStack = new ModalStack();
20
 	private final ModalStack modalStack = new ModalStack();
23
 	private ViewController root;
21
 	private ViewController root;
24
 	private OverlayPresenter overlayPresenter;
22
 	private OverlayPresenter overlayPresenter;
23
+    private NavigationOptions defaultOptions = new NavigationOptions();
25
 
24
 
26
-	public Navigator(final Activity activity) {
25
+    public Navigator(final Activity activity) {
27
 		super(activity, "navigator" + CompatUtils.generateViewId());
26
 		super(activity, "navigator" + CompatUtils.generateViewId());
28
 	}
27
 	}
29
 
28
 
70
 		}
69
 		}
71
 	}
70
 	}
72
 
71
 
72
+    public void setDefaultOptions(NavigationOptions defaultOptions) {
73
+        this.defaultOptions = defaultOptions;
74
+    }
75
+
76
+    public NavigationOptions getDefaultOptions() {
77
+        return defaultOptions;
78
+    }
79
+
73
 	public void setOptions(final String containerId, NavigationOptions options) {
80
 	public void setOptions(final String containerId, NavigationOptions options) {
74
 		ViewController target = findControllerById(containerId);
81
 		ViewController target = findControllerById(containerId);
75
 		if (target instanceof NavigationOptionsListener) {
82
 		if (target instanceof NavigationOptionsListener) {

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

1
 package com.reactnativenavigation.parse;
1
 package com.reactnativenavigation.parse;
2
 
2
 
3
+import android.support.annotation.NonNull;
4
+
3
 import com.reactnativenavigation.BaseTest;
5
 import com.reactnativenavigation.BaseTest;
4
 
6
 
7
+import org.json.JSONException;
5
 import org.json.JSONObject;
8
 import org.json.JSONObject;
6
 import org.junit.Test;
9
 import org.junit.Test;
7
 
10
 
10
 
13
 
11
 public class NavigationOptionsTest extends BaseTest {
14
 public class NavigationOptionsTest extends BaseTest {
12
 
15
 
13
-	@Test
16
+    private static final String TITLE = "the title";
17
+    private static final int TOP_BAR_BACKGROUND_COLOR = 0xff123456;
18
+    private static final int TOP_BAR_TEXT_COLOR = 0xff123456;
19
+    private static final int TOP_BAR_FONT_SIZE = 18;
20
+    private static final String TOP_BAR_FONT_FAMILY = "HelveticaNeue-CondensedBold";
21
+    private static final NavigationOptions.BooleanOptions TOP_BAR_HIDDEN = True;
22
+    private static final NavigationOptions.BooleanOptions BOTTOM_TABS_ANIMATE_HIDE = True;
23
+    private static final NavigationOptions.BooleanOptions BOTTOM_TABS_HIDDEN = True;
24
+    private static final int BOTTOM_TABS_BADGE = 3;
25
+    private static final String BOTTOM_TABS_CURRENT_TAB_ID = "ContainerId";
26
+    private static final int BOTTOM_TABS_CURRENT_TAB_INDEX = 1;
27
+
28
+    @Test
14
 	public void parsesNullAsDefaultEmptyOptions() throws Exception {
29
 	public void parsesNullAsDefaultEmptyOptions() throws Exception {
15
 		assertThat(NavigationOptions.parse(null)).isNotNull();
30
 		assertThat(NavigationOptions.parse(null)).isNotNull();
16
 	}
31
 	}
17
 
32
 
18
 	@Test
33
 	@Test
19
 	public void parsesJson() throws Exception {
34
 	public void parsesJson() throws Exception {
20
-		JSONObject json = new JSONObject();
21
-		JSONObject topBarJson = new JSONObject();
35
+		JSONObject json = new JSONObject()
36
+                .put("topBar", createTopBar())
37
+                .put("tabBar", createTabBar());
38
+		NavigationOptions result = NavigationOptions.parse(json);
39
+        assertResult(result);
40
+	}
22
 
41
 
23
-		topBarJson.put("title", "the title");
24
-		topBarJson.put("backgroundColor", 0xff123456);
25
-		topBarJson.put("textColor", 0xff123456);
26
-		topBarJson.put("textFontSize", 18);
27
-		topBarJson.put("textFontFamily", "HelveticaNeue-CondensedBold");
28
-		topBarJson.put("hidden", true);
42
+    private void assertResult(NavigationOptions result) {
43
+        assertThat(result.topBarOptions.title).isEqualTo(TITLE);
44
+        assertThat(result.topBarOptions.backgroundColor).isEqualTo(TOP_BAR_BACKGROUND_COLOR);
45
+        assertThat(result.topBarOptions.textColor).isEqualTo(TOP_BAR_TEXT_COLOR);
46
+        assertThat(result.topBarOptions.textFontSize).isEqualTo(TOP_BAR_FONT_SIZE);
47
+        assertThat(result.topBarOptions.textFontFamily).isEqualTo(TOP_BAR_FONT_FAMILY);
48
+        assertThat(result.topBarOptions.hidden).isEqualTo(TOP_BAR_HIDDEN);
49
+        assertThat(result.bottomTabsOptions.animateHide).isEqualTo(BOTTOM_TABS_ANIMATE_HIDE);
50
+        assertThat(result.bottomTabsOptions.hidden).isEqualTo(BOTTOM_TABS_HIDDEN);
51
+        assertThat(result.bottomTabsOptions.tabBadge).isEqualTo(BOTTOM_TABS_BADGE);
52
+        assertThat(result.bottomTabsOptions.currentTabId).isEqualTo(BOTTOM_TABS_CURRENT_TAB_ID);
53
+        assertThat(result.bottomTabsOptions.currentTabIndex).isEqualTo(BOTTOM_TABS_CURRENT_TAB_INDEX);
54
+    }
29
 
55
 
30
-		json.put("topBar", topBarJson);
56
+    @NonNull
57
+    private JSONObject createTabBar() throws JSONException {
58
+        return new JSONObject()
59
+            .put("currentTabId", BOTTOM_TABS_CURRENT_TAB_ID)
60
+            .put("currentTabIndex", BOTTOM_TABS_CURRENT_TAB_INDEX)
61
+            .put("hidden", BOTTOM_TABS_HIDDEN)
62
+            .put("animateHide", BOTTOM_TABS_ANIMATE_HIDE)
63
+            .put("tabBadge", BOTTOM_TABS_BADGE);
64
+    }
31
 
65
 
32
-		JSONObject tabBarJson = new JSONObject();
33
-		tabBarJson.put("currentTabId", "ContainerId");
34
-		tabBarJson.put("currentTabIndex", 1);
35
-		tabBarJson.put("hidden", true);
36
-		tabBarJson.put("animateHide", true);
37
-		tabBarJson.put("tabBadge", 3);
66
+    @NonNull
67
+    private JSONObject createTopBar() throws JSONException {
68
+        return new JSONObject()
69
+            .put("title", TITLE)
70
+            .put("backgroundColor", TOP_BAR_BACKGROUND_COLOR)
71
+            .put("textColor", TOP_BAR_TEXT_COLOR)
72
+            .put("textFontSize", TOP_BAR_FONT_SIZE)
73
+            .put("textFontFamily", TOP_BAR_FONT_FAMILY)
74
+            .put("hidden", TOP_BAR_HIDDEN);
75
+    }
38
 
76
 
39
-		json.put("tabBar", tabBarJson);
77
+    @NonNull
78
+    private JSONObject createOtherTopBar() throws JSONException {
79
+        return new JSONObject()
80
+                .put("title", TITLE)
81
+                .put("backgroundColor", TOP_BAR_BACKGROUND_COLOR)
82
+                .put("textColor", TOP_BAR_TEXT_COLOR)
83
+                .put("textFontSize", TOP_BAR_FONT_SIZE)
84
+                .put("textFontFamily", TOP_BAR_FONT_FAMILY)
85
+                .put("hidden", TOP_BAR_HIDDEN);
86
+    }
40
 
87
 
41
-		NavigationOptions result = NavigationOptions.parse(json);
42
-		assertThat(result.topBarOptions.title).isEqualTo("the title");
43
-		assertThat(result.topBarOptions.backgroundColor).isEqualTo(0xff123456);
44
-		assertThat(result.topBarOptions.textColor).isEqualTo(0xff123456);
45
-		assertThat(result.topBarOptions.textFontSize).isEqualTo(18);
46
-		assertThat(result.topBarOptions.textFontFamily).isEqualTo("HelveticaNeue-CondensedBold");
47
-		assertThat(result.topBarOptions.hidden).isEqualTo(True);
48
-		assertThat(result.bottomTabsOptions.animateHide).isEqualTo(True);
49
-		assertThat(result.bottomTabsOptions.hidden).isEqualTo(True);
50
-		assertThat(result.bottomTabsOptions.tabBadge).isEqualTo(3);
51
-		assertThat(result.bottomTabsOptions.currentTabId).isEqualTo("ContainerId");
52
-		assertThat(result.bottomTabsOptions.currentTabIndex).isEqualTo(1);
53
-	}
88
+    @NonNull
89
+    private JSONObject createOtherTabBar() throws JSONException {
90
+        return new JSONObject()
91
+                .put("currentTabId", BOTTOM_TABS_CURRENT_TAB_ID)
92
+                .put("currentTabIndex", BOTTOM_TABS_CURRENT_TAB_INDEX)
93
+                .put("hidden", BOTTOM_TABS_HIDDEN)
94
+                .put("animateHide", BOTTOM_TABS_ANIMATE_HIDE)
95
+                .put("tabBadge", BOTTOM_TABS_BADGE);
96
+    }
97
+
98
+    @Test
99
+    public void mergeDefaultOptions() throws Exception {
100
+        JSONObject json = new JSONObject();
101
+        json.put("topBar", createTopBar());
102
+        json.put("tabBar", createTabBar());
103
+        NavigationOptions defaultOptions = NavigationOptions.parse(json);
104
+        NavigationOptions options = new NavigationOptions();
105
+
106
+        options.mergeWith(defaultOptions);
107
+        assertResult(options);
108
+    }
109
+
110
+    @Test
111
+    public void mergedDefaultOptionsDontOverrideGivenOptions() throws Exception {
112
+        JSONObject defaultJson = new JSONObject()
113
+            .put("topBar", createOtherTopBar())
114
+            .put("tabBar", createOtherTabBar());
115
+        NavigationOptions defaultOptions = NavigationOptions.parse(defaultJson);
116
+
117
+        JSONObject json = new JSONObject()
118
+                .put("topBar", createTopBar())
119
+                .put("tabBar", createTabBar());
120
+        NavigationOptions options = NavigationOptions.parse(json);
121
+        options.withDefaultOptions(defaultOptions);
122
+        assertResult(options);
123
+    }
54
 
124
 
55
 	@Test
125
 	@Test
56
 	public void defaultEmptyOptions() throws Exception {
126
 	public void defaultEmptyOptions() throws Exception {

+ 4
- 0
lib/src/Navigation.js View File

32
     return this.commands.setRoot(params);
32
     return this.commands.setRoot(params);
33
   }
33
   }
34
 
34
 
35
+  setDefaultOptions(options) {
36
+    this.commands.setDefaultOptions(options);
37
+  }
38
+
35
   setOptions(containerId, options) {
39
   setOptions(containerId, options) {
36
     this.commands.setOptions(containerId, options);
40
     this.commands.setOptions(containerId, options);
37
   }
41
   }

+ 4
- 0
lib/src/adapters/NativeCommandsSender.js View File

9
     return this.nativeCommandsModule.setRoot(layoutTree);
9
     return this.nativeCommandsModule.setRoot(layoutTree);
10
   }
10
   }
11
 
11
 
12
+  setDefaultOptions(options) {
13
+    this.nativeCommandsModule.setDefaultOptions(options);
14
+  }
15
+
12
   setOptions(containerId, options) {
16
   setOptions(containerId, options) {
13
     this.nativeCommandsModule.setOptions(containerId, options);
17
     this.nativeCommandsModule.setOptions(containerId, options);
14
   }
18
   }

+ 6
- 0
lib/src/commands/Commands.js View File

15
     return this.nativeCommandsSender.setRoot(layout);
15
     return this.nativeCommandsSender.setRoot(layout);
16
   }
16
   }
17
 
17
 
18
+  setDefaultOptions(options) {
19
+    const input = _.cloneDeep(options);
20
+    OptionsProcessor.processOptions(input);
21
+    this.nativeCommandsSender.setDefaultOptions(input);
22
+  }
23
+
18
   setOptions(containerId, options) {
24
   setOptions(containerId, options) {
19
     const input = _.cloneDeep(options);
25
     const input = _.cloneDeep(options);
20
     OptionsProcessor.processOptions(input);
26
     OptionsProcessor.processOptions(input);

+ 8
- 0
lib/src/commands/Commands.test.js View File

77
     });
77
     });
78
   });
78
   });
79
 
79
 
80
+  describe('setDefaultOptions', () => {
81
+    it('deep clones input to avoid mutation errors', () => {
82
+      const obj = { title: 'test' };
83
+      uut.setDefaultOptions(obj);
84
+      expect(mockCommandsSender.setDefaultOptions.mock.calls[0][0]).not.toBe(obj);
85
+    });
86
+  });
87
+
80
   describe('showModal', () => {
88
   describe('showModal', () => {
81
     it('sends command to native after parsing into a correct layout tree', () => {
89
     it('sends command to native after parsing into a correct layout tree', () => {
82
       uut.showModal({
90
       uut.showModal({