Browse Source

Stack layout android (#2531)

* Change topBar backgroundColor to Color object

* StackLayout implementation

Unfortunately, this commit also contains a lot of refactoring and a few
bug fixes relating to styles.

* TopBar is not part of Component, instead it's a part of StackLayout
  and shared between all components pushed to the stack Similar to iOS.
* Handle ScrollEvent in ReactView instead of passing the listener down
* Implement Fraction and Text null objects for style params
* Converted a few style params to null objects. This fixes some styles
  being applied even when they were not specified explicitly.
* Stop sending null promise to commands
* Seperate push and pop into animatePush/push and animatePop/pop
  to avoid boolean param

* Adapt TopTabs to new API
Guy Carmeli 6 years ago
parent
commit
bb77b8ca4a
No account linked to committer's email address
51 changed files with 785 additions and 684 deletions
  1. 5
    5
      lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarAnimator.java
  2. 1
    2
      lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarCollapseBehavior.java
  3. 7
    7
      lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabsOptions.java
  4. 15
    16
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Button.java
  5. 0
    5
      lib/android/app/src/main/java/com/reactnativenavigation/parse/DEFAULT_VALUES.java
  6. 7
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Fraction.java
  7. 9
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/FractionParser.java
  8. 6
    4
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java
  9. 12
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/NullFraction.java
  10. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/NullNumber.java
  11. 12
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/NullText.java
  12. 6
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java
  13. 4
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Param.java
  14. 7
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Text.java
  15. 9
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TextParser.java
  16. 17
    18
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java
  17. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabOptions.java
  18. 10
    11
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java
  19. 37
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/NoOpPromise.java
  20. 20
    22
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/BottomTabsController.java
  21. 6
    8
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java
  22. 0
    104
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ContainerViewController.java
  23. 8
    38
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java
  24. 16
    5
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java
  25. 79
    64
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java
  26. 12
    9
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java
  27. 2
    3
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabController.java
  28. 8
    5
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsAdapter.java
  29. 5
    5
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java
  30. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsViewPager.java
  31. 1
    3
      lib/android/app/src/main/java/com/reactnativenavigation/views/Component.java
  32. 6
    22
      lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentLayout.java
  33. 46
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/StackLayout.java
  34. 6
    6
      lib/android/app/src/main/java/com/reactnativenavigation/views/TitleBarButton.java
  35. 22
    16
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopBar.java
  36. 16
    28
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsLayout.java
  37. 3
    3
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsLayoutCreator.java
  38. 10
    17
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestComponentLayout.java
  39. 8
    8
      lib/android/app/src/test/java/com/reactnativenavigation/parse/NavigationOptionsTest.java
  40. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsControllerTest.java
  41. 16
    13
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ComponentViewControllerTest.java
  42. 83
    67
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java
  43. 71
    56
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java
  44. 3
    3
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ParentControllerTest.java
  45. 141
    93
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackControllerTest.java
  46. 11
    6
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java
  47. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ViewControllerTest.java
  48. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarTest.java
  49. 2
    1
      playground/src/screens/OptionsScreen.js
  50. 6
    1
      playground/src/screens/PushedScreen.js
  51. 6
    1
      playground/src/screens/WelcomeScreen.js

+ 5
- 5
lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarAnimator.java View File

63
     }
63
     }
64
 
64
 
65
     void hide(float startTranslation, TimeInterpolator interpolator, int duration) {
65
     void hide(float startTranslation, TimeInterpolator interpolator, int duration) {
66
-        ObjectAnimator topbarAnim = ObjectAnimator.ofFloat(topBar, View.TRANSLATION_Y, startTranslation, -1 * topBar.getMeasuredHeight());
67
-        topbarAnim.setInterpolator(interpolator);
68
-        topbarAnim.setDuration(duration);
66
+        ObjectAnimator animator = ObjectAnimator.ofFloat(topBar, View.TRANSLATION_Y, startTranslation, -1 * topBar.getMeasuredHeight());
67
+        animator.setInterpolator(interpolator);
68
+        animator.setDuration(duration);
69
 
69
 
70
-        topbarAnim.addListener(new AnimatorListenerAdapter() {
70
+        animator.addListener(new AnimatorListenerAdapter() {
71
             @Override
71
             @Override
72
             public void onAnimationEnd(Animator animation) {
72
             public void onAnimationEnd(Animator animation) {
73
                 if (contentView != null) {
73
                 if (contentView != null) {
79
                 topBar.setVisibility(View.GONE);
79
                 topBar.setVisibility(View.GONE);
80
             }
80
             }
81
         });
81
         });
82
-        topbarAnim.start();
82
+        animator.start();
83
     }
83
     }
84
 }
84
 }

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

11
     private ScrollEventListener scrollEventListener;
11
     private ScrollEventListener scrollEventListener;
12
     private TopBarAnimator animator;
12
     private TopBarAnimator animator;
13
 
13
 
14
-    public TopBarCollapseBehavior(TopBar topBar, ScrollEventListener scrollEventListener) {
14
+    public TopBarCollapseBehavior(TopBar topBar) {
15
         this.topBar = topBar;
15
         this.topBar = topBar;
16
         this.animator = new TopBarAnimator(topBar);
16
         this.animator = new TopBarAnimator(topBar);
17
-        this.scrollEventListener = scrollEventListener;
18
     }
17
     }
19
 
18
 
20
     public void enableCollapse() {
19
     public void enableCollapse() {

+ 7
- 7
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 = json.optString("currentTabId", NO_VALUE);
14
+		options.currentTabId = TextParser.parse(json, "currentTabId");
15
 		options.currentTabIndex = json.optInt("currentTabIndex", NO_INT_VALUE);
15
 		options.currentTabIndex = json.optInt("currentTabIndex", NO_INT_VALUE);
16
 		options.tabBadge = json.optInt("tabBadge", NO_INT_VALUE);
16
 		options.tabBadge = json.optInt("tabBadge", NO_INT_VALUE);
17
 		options.hidden = BooleanOptions.parse(json.optString("hidden"));
17
 		options.hidden = BooleanOptions.parse(json.optString("hidden"));
20
 		return options;
20
 		return options;
21
 	}
21
 	}
22
 
22
 
23
-	public int tabBadge = NO_INT_VALUE;
24
-	public BooleanOptions hidden = BooleanOptions.False;
25
-	public BooleanOptions animateHide = BooleanOptions.False;
23
+	int tabBadge = NO_INT_VALUE;
24
+	BooleanOptions hidden = BooleanOptions.False;
25
+	BooleanOptions animateHide = BooleanOptions.False;
26
 	public int currentTabIndex = NO_INT_VALUE;
26
 	public int currentTabIndex = NO_INT_VALUE;
27
-	public String currentTabId = NO_VALUE;
27
+	public Text currentTabId = new NullText();
28
 
28
 
29
 	void mergeWith(final BottomTabsOptions other) {
29
 	void mergeWith(final BottomTabsOptions other) {
30
-		if (!NO_VALUE.equals(other.currentTabId)) {
30
+		if (other.currentTabId.hasValue()) {
31
 			currentTabId = other.currentTabId;
31
 			currentTabId = other.currentTabId;
32
 		}
32
 		}
33
 		if (NO_INT_VALUE != other.currentTabIndex) {
33
 		if (NO_INT_VALUE != other.currentTabIndex) {
45
 	}
45
 	}
46
 
46
 
47
     void mergeWithDefault(final BottomTabsOptions defaultOptions) {
47
     void mergeWithDefault(final BottomTabsOptions defaultOptions) {
48
-        if (NO_VALUE.equals(currentTabId)) {
48
+        if (!currentTabId.hasValue()) {
49
             currentTabId = defaultOptions.currentTabId;
49
             currentTabId = defaultOptions.currentTabId;
50
         }
50
         }
51
         if (NO_INT_VALUE == currentTabIndex) {
51
         if (NO_INT_VALUE == currentTabIndex) {

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

8
 
8
 
9
 import java.util.ArrayList;
9
 import java.util.ArrayList;
10
 
10
 
11
-import static com.reactnativenavigation.parse.DEFAULT_VALUES.NO_VALUE;
12
 import static com.reactnativenavigation.parse.Options.NO_INT_VALUE;
11
 import static com.reactnativenavigation.parse.Options.NO_INT_VALUE;
13
 
12
 
14
 public class Button {
13
 public class Button {
15
 	public String id;
14
 	public String id;
16
-	public String title;
15
+	public Text title;
17
 	public Options.BooleanOptions disabled;
16
 	public Options.BooleanOptions disabled;
18
 	public Options.BooleanOptions disableIconTint;
17
 	public Options.BooleanOptions disableIconTint;
19
 	public int showAsAction;
18
 	public int showAsAction;
20
 	@ColorInt public int buttonColor;
19
 	@ColorInt public int buttonColor;
21
 	public int buttonFontSize;
20
 	public int buttonFontSize;
22
-	public String buttonFontWeight;
23
-	public String icon;
21
+	public Text buttonFontWeight;
22
+	public Text icon;
24
 
23
 
25
 	private static Button parseJson(JSONObject json)  {
24
 	private static Button parseJson(JSONObject json)  {
26
 		Button button = new Button();
25
 		Button button = new Button();
27
 		button.id = json.optString("id");
26
 		button.id = json.optString("id");
28
-		button.title = json.optString("title", NO_VALUE);
29
-		button.disabled = Options.BooleanOptions.parse(json.optString("disabled", NO_VALUE));
30
-		button.disableIconTint = Options.BooleanOptions.parse(json.optString("disableIconTint", NO_VALUE));
31
-		button.showAsAction = parseShowAsAction(json.optString("showAsAction", NO_VALUE));
27
+		button.title = TextParser.parse(json, "title");
28
+		button.disabled = Options.BooleanOptions.parse(json.optString("disabled", ""));
29
+		button.disableIconTint = Options.BooleanOptions.parse(json.optString("disableIconTint", ""));
30
+		button.showAsAction = parseShowAsAction(json);
32
 		button.buttonColor = json.optInt("buttonColor", NO_INT_VALUE);
31
 		button.buttonColor = json.optInt("buttonColor", NO_INT_VALUE);
33
 		button.buttonFontSize = json.optInt("buttonFontSize", NO_INT_VALUE);
32
 		button.buttonFontSize = json.optInt("buttonFontSize", NO_INT_VALUE);
34
-		button.buttonFontWeight = json.optString("buttonFontWeight", NO_VALUE);
33
+		button.buttonFontWeight = TextParser.parse(json, "buttonFontWeight");
35
 
34
 
36
-		JSONObject iconObject = json.optJSONObject("icon");
37
-		if (iconObject != null) {
38
-			button.icon = iconObject.optString("uri", NO_VALUE);
35
+		if (json.has("icon")) {
36
+			button.icon = TextParser.parse(json.optJSONObject("icon"), "uri");
39
 		}
37
 		}
40
 
38
 
41
 		return button;
39
 		return button;
42
 	}
40
 	}
43
 
41
 
44
-	public static ArrayList<Button> parseJsonArray(JSONArray jsonArray) {
42
+	static ArrayList<Button> parseJsonArray(JSONArray jsonArray) {
45
 		ArrayList<Button> buttons = new ArrayList<>();
43
 		ArrayList<Button> buttons = new ArrayList<>();
46
 
44
 
47
 		if (jsonArray == null) {
45
 		if (jsonArray == null) {
57
 		return buttons;
55
 		return buttons;
58
 	}
56
 	}
59
 
57
 
60
-	private static int parseShowAsAction(String showAsAction) {
61
-		if (NO_VALUE.equals(showAsAction)) {
58
+	private static int parseShowAsAction(JSONObject json) {
59
+	    final Text showAsAction = TextParser.parse(json, "showAsAction");
60
+		if (!showAsAction.hasValue()) {
62
 			return MenuItem.SHOW_AS_ACTION_IF_ROOM;
61
 			return MenuItem.SHOW_AS_ACTION_IF_ROOM;
63
 		}
62
 		}
64
 
63
 
65
-		switch (showAsAction) {
64
+		switch (showAsAction.get()) {
66
 			case "always":
65
 			case "always":
67
 				return MenuItem.SHOW_AS_ACTION_ALWAYS;
66
 				return MenuItem.SHOW_AS_ACTION_ALWAYS;
68
 			case "never":
67
 			case "never":

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

1
 package com.reactnativenavigation.parse;
1
 package com.reactnativenavigation.parse;
2
 
2
 
3
 
3
 
4
-import android.graphics.Color;
5
-
6
 public interface DEFAULT_VALUES {
4
 public interface DEFAULT_VALUES {
7
-	String NO_VALUE = "";
8
 	int NO_INT_VALUE = Integer.MIN_VALUE;
5
 	int NO_INT_VALUE = Integer.MIN_VALUE;
9
-	float NO_FLOAT_VALUE = Float.MIN_VALUE;
10
-	int NO_COLOR_VALUE = Color.TRANSPARENT;
11
 }
6
 }

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

1
+package com.reactnativenavigation.parse;
2
+
3
+public class Fraction extends Param<Float> {
4
+    public Fraction(float value) {
5
+        super(value);
6
+    }
7
+}

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

1
+package com.reactnativenavigation.parse;
2
+
3
+import org.json.JSONObject;
4
+
5
+public class FractionParser {
6
+    public static Fraction parse(JSONObject json, String fraction) {
7
+        return json.has(fraction) ? new Fraction(json.optInt(fraction)) : new NullFraction();
8
+    }
9
+}

+ 6
- 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.NoOpPromise;
6
 import com.reactnativenavigation.utils.TypefaceLoader;
7
 import com.reactnativenavigation.utils.TypefaceLoader;
7
 import com.reactnativenavigation.viewcontrollers.BottomTabsController;
8
 import com.reactnativenavigation.viewcontrollers.BottomTabsController;
8
 import com.reactnativenavigation.viewcontrollers.ComponentViewController;
9
 import com.reactnativenavigation.viewcontrollers.ComponentViewController;
102
 	private ViewController createStack(LayoutNode node) {
103
 	private ViewController createStack(LayoutNode node) {
103
 		StackController stackController = new StackController(activity, node.id);
104
 		StackController stackController = new StackController(activity, node.id);
104
 		for (LayoutNode child : node.children) {
105
 		for (LayoutNode child : node.children) {
105
-			stackController.push(create(child), null);
106
+			stackController.animatePush(create(child), new NoOpPromise());
106
 		}
107
 		}
107
 		return stackController;
108
 		return stackController;
108
 	}
109
 	}
118
 	}
119
 	}
119
 
120
 
120
     private ViewController createTopTabs(LayoutNode node) {
121
     private ViewController createTopTabs(LayoutNode node) {
121
-        final List<TopTabController> tabs = new ArrayList<>();
122
+        final List<ViewController> tabs = new ArrayList<>();
122
         for (int i = 0; i < node.children.size(); i++) {
123
         for (int i = 0; i < node.children.size(); i++) {
123
-            TopTabController tabController = (TopTabController) create(node.children.get(i));
124
-            tabController.setTabIndex(i);
124
+            ViewController tabController = create(node.children.get(i));
125
+            Options options = Options.parse(typefaceManager, node.children.get(i).getNavigationOptions(), defaultOptions);
126
+            options.setTopTabIndex(i);
125
             tabs.add(tabController);
127
             tabs.add(tabController);
126
         }
128
         }
127
         Options options = Options.parse(typefaceManager, node.getNavigationOptions(), defaultOptions);
129
         Options options = Options.parse(typefaceManager, node.getNavigationOptions(), defaultOptions);

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

1
+package com.reactnativenavigation.parse;
2
+
3
+public class NullFraction extends Fraction {
4
+    NullFraction() {
5
+        super(0);
6
+    }
7
+
8
+    @Override
9
+    public boolean hasValue() {
10
+        return false;
11
+    }
12
+}

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

1
 package com.reactnativenavigation.parse;
1
 package com.reactnativenavigation.parse;
2
 
2
 
3
 public class NullNumber extends Number {
3
 public class NullNumber extends Number {
4
-    public NullNumber() {
4
+    NullNumber() {
5
         super(0);
5
         super(0);
6
     }
6
     }
7
 
7
 

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

1
+package com.reactnativenavigation.parse;
2
+
3
+public class NullText extends Text {
4
+    public NullText() {
5
+        super("");
6
+    }
7
+
8
+    @Override
9
+    public boolean hasValue() {
10
+        return false;
11
+    }
12
+}

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

1
 package com.reactnativenavigation.parse;
1
 package com.reactnativenavigation.parse;
2
 
2
 
3
 import android.support.annotation.NonNull;
3
 import android.support.annotation.NonNull;
4
+import android.text.TextUtils;
4
 
5
 
5
 import com.reactnativenavigation.utils.TypefaceLoader;
6
 import com.reactnativenavigation.utils.TypefaceLoader;
6
 
7
 
14
 		NoValue;
15
 		NoValue;
15
 
16
 
16
 		static BooleanOptions parse(String value) {
17
 		static BooleanOptions parse(String value) {
17
-			if (value != null && !value.equals("")) {
18
+			if (!TextUtils.isEmpty(value)) {
18
 				return Boolean.valueOf(value) ? True : False;
19
 				return Boolean.valueOf(value) ? True : False;
19
 			}
20
 			}
20
 			return NoValue;
21
 			return NoValue;
44
     @NonNull public TopTabOptions topTabOptions = new TopTabOptions();
45
     @NonNull public TopTabOptions topTabOptions = new TopTabOptions();
45
     @NonNull public BottomTabsOptions bottomTabsOptions = new BottomTabsOptions();
46
     @NonNull public BottomTabsOptions bottomTabsOptions = new BottomTabsOptions();
46
 
47
 
48
+    void setTopTabIndex(int i) {
49
+        topTabOptions.tabIndex = i;
50
+    }
51
+
47
 	public void mergeWith(final Options other) {
52
 	public void mergeWith(final Options other) {
48
         topBarOptions.mergeWith(other.topBarOptions);
53
         topBarOptions.mergeWith(other.topBarOptions);
49
         topTabsOptions.mergeWith(other.topTabsOptions);
54
         topTabsOptions.mergeWith(other.topTabsOptions);

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

14
         throw new RuntimeException("Tried to get null value!");
14
         throw new RuntimeException("Tried to get null value!");
15
     }
15
     }
16
 
16
 
17
+    public T get(T defaultValue) {
18
+        return hasValue() ? value : defaultValue;
19
+    }
20
+
17
     public boolean hasValue() {
21
     public boolean hasValue() {
18
         return value != null;
22
         return value != null;
19
     }
23
     }

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

1
+package com.reactnativenavigation.parse;
2
+
3
+public class Text extends Param<String> {
4
+    public Text(String value) {
5
+        super(value);
6
+    }
7
+}

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

1
+package com.reactnativenavigation.parse;
2
+
3
+import org.json.JSONObject;
4
+
5
+public class TextParser {
6
+    public static Text parse(JSONObject json, String text) {
7
+        return json.has(text) ? new Text(json.optString(text)) : new NullText();
8
+    }
9
+}

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

2
 
2
 
3
 
3
 
4
 import android.graphics.Typeface;
4
 import android.graphics.Typeface;
5
-import android.support.annotation.ColorInt;
6
 import android.support.annotation.Nullable;
5
 import android.support.annotation.Nullable;
7
 
6
 
8
 import com.reactnativenavigation.utils.TypefaceLoader;
7
 import com.reactnativenavigation.utils.TypefaceLoader;
17
         TopBarOptions options = new TopBarOptions();
16
         TopBarOptions options = new TopBarOptions();
18
         if (json == null) return options;
17
         if (json == null) return options;
19
 
18
 
20
-        options.title = json.optString("title", NO_VALUE);
21
-        options.backgroundColor = json.optInt("backgroundColor", NO_COLOR_VALUE);
22
-        options.textColor = json.optInt("textColor", NO_COLOR_VALUE);
23
-        options.textFontSize = (float) json.optDouble("textFontSize", NO_FLOAT_VALUE);
24
-        options.textFontFamily = typefaceManager.getTypeFace(json.optString("textFontFamily", NO_VALUE));
19
+        options.title = TextParser.parse(json, "title");
20
+        options.backgroundColor = ColorParser.parse(json, "backgroundColor");
21
+        options.textColor = ColorParser.parse(json, "textColor");
22
+        options.textFontSize = FractionParser.parse(json, "textFontSize");
23
+        options.textFontFamily = typefaceManager.getTypeFace(json.optString("textFontFamily", ""));
25
         options.hidden = Options.BooleanOptions.parse(json.optString("hidden"));
24
         options.hidden = Options.BooleanOptions.parse(json.optString("hidden"));
26
         options.animateHide = Options.BooleanOptions.parse(json.optString("animateHide"));
25
         options.animateHide = Options.BooleanOptions.parse(json.optString("animateHide"));
27
         options.hideOnScroll = Options.BooleanOptions.parse(json.optString("hideOnScroll"));
26
         options.hideOnScroll = Options.BooleanOptions.parse(json.optString("hideOnScroll"));
32
         return options;
31
         return options;
33
     }
32
     }
34
 
33
 
35
-    public String title = NO_VALUE;
36
-    @ColorInt public int backgroundColor = NO_COLOR_VALUE;
37
-    @ColorInt public int textColor = NO_COLOR_VALUE;
38
-    public float textFontSize = NO_FLOAT_VALUE;
34
+    public Text title = new NullText();
35
+    public Color backgroundColor = new NullColor();
36
+    public Color textColor = new NullColor();
37
+    public Fraction textFontSize = new NullFraction();
39
     @Nullable public Typeface textFontFamily;
38
     @Nullable public Typeface textFontFamily;
40
     public Options.BooleanOptions hidden = Options.BooleanOptions.NoValue;
39
     public Options.BooleanOptions hidden = Options.BooleanOptions.NoValue;
41
     public Options.BooleanOptions animateHide = Options.BooleanOptions.NoValue;
40
     public Options.BooleanOptions animateHide = Options.BooleanOptions.NoValue;
45
     public ArrayList<Button> rightButtons;
44
     public ArrayList<Button> rightButtons;
46
 
45
 
47
     void mergeWith(final TopBarOptions other) {
46
     void mergeWith(final TopBarOptions other) {
48
-        if (!NO_VALUE.equals(other.title)) title = other.title;
49
-        if (other.backgroundColor != NO_COLOR_VALUE)
47
+        if (other.title != null) title = other.title;
48
+        if (other.backgroundColor.hasValue())
50
             backgroundColor = other.backgroundColor;
49
             backgroundColor = other.backgroundColor;
51
-        if (other.textColor != NO_COLOR_VALUE)
50
+        if (other.textColor.hasValue())
52
             textColor = other.textColor;
51
             textColor = other.textColor;
53
-        if (other.textFontSize != NO_FLOAT_VALUE)
52
+        if (other.textFontSize.hasValue())
54
             textFontSize = other.textFontSize;
53
             textFontSize = other.textFontSize;
55
         if (other.textFontFamily != null)
54
         if (other.textFontFamily != null)
56
             textFontFamily = other.textFontFamily;
55
             textFontFamily = other.textFontFamily;
73
     }
72
     }
74
 
73
 
75
     void mergeWithDefault(TopBarOptions defaultOptions) {
74
     void mergeWithDefault(TopBarOptions defaultOptions) {
76
-        if (NO_VALUE.equals(title))
75
+        if (title == null)
77
             title = defaultOptions.title;
76
             title = defaultOptions.title;
78
-        if (backgroundColor == NO_COLOR_VALUE)
77
+        if (!backgroundColor.hasValue())
79
             backgroundColor = defaultOptions.backgroundColor;
78
             backgroundColor = defaultOptions.backgroundColor;
80
-        if (textColor == NO_COLOR_VALUE)
79
+        if (!textColor.hasValue())
81
             textColor = defaultOptions.textColor;
80
             textColor = defaultOptions.textColor;
82
-        if (textFontSize == NO_FLOAT_VALUE)
81
+        if (!textFontSize.hasValue())
83
             textFontSize = defaultOptions.textFontSize;
82
             textFontSize = defaultOptions.textFontSize;
84
         if (textFontFamily == null)
83
         if (textFontFamily == null)
85
             textFontFamily = defaultOptions.textFontFamily;
84
             textFontFamily = defaultOptions.textFontFamily;

+ 2
- 2
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabOptions.java View File

8
 import org.json.JSONObject;
8
 import org.json.JSONObject;
9
 
9
 
10
 public class TopTabOptions implements DEFAULT_VALUES {
10
 public class TopTabOptions implements DEFAULT_VALUES {
11
-    public String title = NO_VALUE;
11
+    public Text title = new NullText();
12
     @Nullable public Typeface fontFamily;
12
     @Nullable public Typeface fontFamily;
13
     public int tabIndex;
13
     public int tabIndex;
14
 
14
 
16
         TopTabOptions result = new TopTabOptions();
16
         TopTabOptions result = new TopTabOptions();
17
         if (json == null) return result;
17
         if (json == null) return result;
18
 
18
 
19
-        result.title = json.optString("title", NO_VALUE);
19
+        result.title = TextParser.parse(json, "title");
20
         result.fontFamily = typefaceManager.getTypeFace(json.optString("titleFontFamily"));
20
         result.fontFamily = typefaceManager.getTypeFace(json.optString("titleFontFamily"));
21
         return result;
21
         return result;
22
     }
22
     }

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

5
 import com.reactnativenavigation.parse.TopBarOptions;
5
 import com.reactnativenavigation.parse.TopBarOptions;
6
 import com.reactnativenavigation.parse.TopTabOptions;
6
 import com.reactnativenavigation.parse.TopTabOptions;
7
 import com.reactnativenavigation.parse.TopTabsOptions;
7
 import com.reactnativenavigation.parse.TopTabsOptions;
8
-import com.reactnativenavigation.views.Component;
8
+import com.reactnativenavigation.views.ReactComponent;
9
 import com.reactnativenavigation.views.TopBar;
9
 import com.reactnativenavigation.views.TopBar;
10
 
10
 
11
 import java.util.ArrayList;
11
 import java.util.ArrayList;
14
 import static com.reactnativenavigation.parse.Options.BooleanOptions.True;
14
 import static com.reactnativenavigation.parse.Options.BooleanOptions.True;
15
 
15
 
16
 public class OptionsPresenter {
16
 public class OptionsPresenter {
17
-
18
-    private Component reactComponent;
19
     private TopBar topBar;
17
     private TopBar topBar;
18
+    private ReactComponent component;
20
 
19
 
21
-    public OptionsPresenter(Component reactComponent) {
22
-        this.reactComponent = reactComponent;
23
-        this.topBar = reactComponent.getTopBar();
20
+    public OptionsPresenter(TopBar topBar, ReactComponent component) {
21
+        this.topBar = topBar;
22
+        this.component = component;
24
     }
23
     }
25
 
24
 
26
     public void applyOptions(Options options) {
25
     public void applyOptions(Options options) {
31
     }
30
     }
32
 
31
 
33
     private void applyTopBarOptions(TopBarOptions options) {
32
     private void applyTopBarOptions(TopBarOptions options) {
34
-        topBar.setTitle(options.title);
33
+        if (options.title.hasValue()) topBar.setTitle(options.title.get());
35
         topBar.setBackgroundColor(options.backgroundColor);
34
         topBar.setBackgroundColor(options.backgroundColor);
36
         topBar.setTitleTextColor(options.textColor);
35
         topBar.setTitleTextColor(options.textColor);
37
         topBar.setTitleFontSize(options.textFontSize);
36
         topBar.setTitleFontSize(options.textFontSize);
38
 
37
 
39
         topBar.setTitleTypeface(options.textFontFamily);
38
         topBar.setTitleTypeface(options.textFontFamily);
40
-        if (options.hidden == Options.BooleanOptions.True) {
39
+        if (options.hidden == True) {
41
             topBar.hide(options.animateHide);
40
             topBar.hide(options.animateHide);
42
         }
41
         }
43
-        if (options.hidden == Options.BooleanOptions.False) {
42
+        if (options.hidden == False) {
44
             topBar.show(options.animateHide);
43
             topBar.show(options.animateHide);
45
         }
44
         }
46
         if (options.drawBehind == True) {
45
         if (options.drawBehind == True) {
47
-            reactComponent.drawBehindTopBar();
46
+            component.drawBehindTopBar();
48
         } else if (options.drawBehind == False) {
47
         } else if (options.drawBehind == False) {
49
-            reactComponent.drawBelowTopBar();
48
+            component.drawBelowTopBar(topBar);
50
         }
49
         }
51
 
50
 
52
         if (options.hideOnScroll == True) {
51
         if (options.hideOnScroll == True) {

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

1
+package com.reactnativenavigation.utils;
2
+
3
+import com.facebook.react.bridge.Promise;
4
+
5
+import javax.annotation.Nullable;
6
+
7
+public class NoOpPromise implements Promise {
8
+    @Override
9
+    public void resolve(@Nullable Object value) {
10
+
11
+    }
12
+
13
+    @Override
14
+    public void reject(String code, String message) {
15
+
16
+    }
17
+
18
+    @Override
19
+    public void reject(String code, Throwable e) {
20
+
21
+    }
22
+
23
+    @Override
24
+    public void reject(String code, String message, Throwable e) {
25
+
26
+    }
27
+
28
+    @Override
29
+    public void reject(String message) {
30
+
31
+    }
32
+
33
+    @Override
34
+    public void reject(Throwable reason) {
35
+
36
+    }
37
+}

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

11
 import android.widget.RelativeLayout;
11
 import android.widget.RelativeLayout;
12
 
12
 
13
 import com.reactnativenavigation.parse.Options;
13
 import com.reactnativenavigation.parse.Options;
14
+import com.reactnativenavigation.parse.Text;
14
 import com.reactnativenavigation.presentation.NavigationOptionsListener;
15
 import com.reactnativenavigation.presentation.NavigationOptionsListener;
15
 import com.reactnativenavigation.utils.CompatUtils;
16
 import com.reactnativenavigation.utils.CompatUtils;
16
 
17
 
23
 import static android.widget.RelativeLayout.ABOVE;
24
 import static android.widget.RelativeLayout.ABOVE;
24
 import static android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM;
25
 import static android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM;
25
 import static com.reactnativenavigation.parse.DEFAULT_VALUES.NO_INT_VALUE;
26
 import static com.reactnativenavigation.parse.DEFAULT_VALUES.NO_INT_VALUE;
26
-import static com.reactnativenavigation.parse.DEFAULT_VALUES.NO_VALUE;
27
 
27
 
28
 public class BottomTabsController extends ParentController
28
 public class BottomTabsController extends ParentController
29
 		implements BottomNavigationView.OnNavigationItemSelectedListener, NavigationOptionsListener {
29
 		implements BottomNavigationView.OnNavigationItemSelectedListener, NavigationOptionsListener {
60
 		return true;
60
 		return true;
61
 	}
61
 	}
62
 
62
 
63
-	public void selectTabAtIndex(final int newIndex) {
63
+	void selectTabAtIndex(final int newIndex) {
64
 		tabs.get(selectedIndex).getView().setVisibility(View.GONE);
64
 		tabs.get(selectedIndex).getView().setVisibility(View.GONE);
65
 		selectedIndex = newIndex;
65
 		selectedIndex = newIndex;
66
 		tabs.get(selectedIndex).getView().setVisibility(View.VISIBLE);
66
 		tabs.get(selectedIndex).getView().setVisibility(View.VISIBLE);
87
 		getView().addView(tab.getView(), params);
87
 		getView().addView(tab.getView(), params);
88
 	}
88
 	}
89
 
89
 
90
-	public int getSelectedIndex() {
90
+	int getSelectedIndex() {
91
 		return selectedIndex;
91
 		return selectedIndex;
92
 	}
92
 	}
93
 
93
 
99
 
99
 
100
 	@Override
100
 	@Override
101
 	public void mergeOptions(Options options) {
101
 	public void mergeOptions(Options options) {
102
-		if (options.bottomTabsOptions != null) {
103
-			if (options.bottomTabsOptions.currentTabIndex != NO_INT_VALUE) {
104
-				selectTabAtIndex(options.bottomTabsOptions.currentTabIndex);
105
-			}
106
-			if (!NO_VALUE.equals(options.bottomTabsOptions.currentTabId)) {
107
-				String id = options.bottomTabsOptions.currentTabId;
108
-				for (ViewController controller : tabs) {
109
-					if (controller.getId().equals(id)) {
110
-						selectTabAtIndex(tabs.indexOf(controller));
111
-					}
112
-					if (controller instanceof StackController) {
113
-						if (hasControlWithId((StackController) controller, id)) {
114
-							selectTabAtIndex(tabs.indexOf(controller));
115
-						}
116
-					}
117
-				}
118
-			}
119
-		}
120
-	}
102
+        if (options.bottomTabsOptions.currentTabIndex != NO_INT_VALUE) {
103
+            selectTabAtIndex(options.bottomTabsOptions.currentTabIndex);
104
+        }
105
+        if (options.bottomTabsOptions.currentTabId.hasValue()) {
106
+            Text id = options.bottomTabsOptions.currentTabId;
107
+            for (ViewController controller : tabs) {
108
+                if (controller.getId().equals(id.get())) {
109
+                    selectTabAtIndex(tabs.indexOf(controller));
110
+                }
111
+                if (controller instanceof StackController) {
112
+                    if (hasControlWithId((StackController) controller, id.get())) {
113
+                        selectTabAtIndex(tabs.indexOf(controller));
114
+                    }
115
+                }
116
+            }
117
+        }
118
+    }
121
 
119
 
122
 	private boolean hasControlWithId(StackController controller, String id) {
120
 	private boolean hasControlWithId(StackController controller, String id) {
123
 		for (ViewController child : controller.getChildControllers()) {
121
 		for (ViewController child : controller.getChildControllers()) {

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

36
     private final String componentName;
36
     private final String componentName;
37
 
37
 
38
     private final ReactViewCreator viewCreator;
38
     private final ReactViewCreator viewCreator;
39
-    private Options options;
40
     private ReactComponent component;
39
     private ReactComponent component;
41
 
40
 
42
     public ComponentViewController(final Activity activity,
41
     public ComponentViewController(final Activity activity,
47
         super(activity, id);
46
         super(activity, id);
48
         this.componentName = componentName;
47
         this.componentName = componentName;
49
         this.viewCreator = viewCreator;
48
         this.viewCreator = viewCreator;
50
-        this.options = initialNavigationOptions;
51
-    }
52
-
53
-    @RestrictTo(RestrictTo.Scope.TESTS)
54
-    TopBar getTopBar() {
55
-        return component.getTopBar();
49
+        options = initialNavigationOptions;
56
     }
50
     }
57
 
51
 
58
     @Override
52
     @Override
66
     public void onViewAppeared() {
60
     public void onViewAppeared() {
67
         super.onViewAppeared();
61
         super.onViewAppeared();
68
         ensureViewIsCreated();
62
         ensureViewIsCreated();
69
-        component.applyOptions(options);
63
+        applyOnParentStack(parentController -> {
64
+            parentController.clearOptions();
65
+            parentController.applyOptions(options, component);
66
+        });
70
         component.sendComponentStart();
67
         component.sendComponentStart();
71
     }
68
     }
72
 
69
 
92
     public void mergeOptions(Options options) {
89
     public void mergeOptions(Options options) {
93
         this.options.mergeWith(options);
90
         this.options.mergeWith(options);
94
         component.applyOptions(this.options);
91
         component.applyOptions(this.options);
92
+        applyOnParentStack(parentController -> parentController.applyOptions(this.options, component));
95
     }
93
     }
96
 
94
 
97
     Options getOptions() {
95
     Options getOptions() {

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

1
-package com.reactnativenavigation.viewcontrollers;
2
-
3
-import android.app.Activity;
4
-import android.support.annotation.NonNull;
5
-import android.support.annotation.RestrictTo;
6
-import android.view.View;
7
-
8
-import com.reactnativenavigation.parse.Options;
9
-import com.reactnativenavigation.presentation.NavigationOptionsListener;
10
-import com.reactnativenavigation.views.ReactComponent;
11
-import com.reactnativenavigation.views.TopBar;
12
-
13
-public class ContainerViewController extends ViewController implements NavigationOptionsListener {
14
-
15
-    public interface ReactViewCreator {
16
-
17
-        IReactView create(Activity activity, String containerId, String containerName);
18
-    }
19
-
20
-    public interface IReactView {
21
-
22
-        boolean isReady();
23
-
24
-        View asView();
25
-
26
-        void destroy();
27
-
28
-        void sendComponentStart();
29
-
30
-        void sendComponentStop();
31
-
32
-        void sendOnNavigationButtonPressed(String buttonId);
33
-    }
34
-
35
-    private final String componentName;
36
-
37
-    private final ReactViewCreator viewCreator;
38
-    private Options options;
39
-    private ReactComponent component;
40
-
41
-    public ContainerViewController(final Activity activity,
42
-                                   final String id,
43
-                                   final String componentName,
44
-                                   final ReactViewCreator viewCreator,
45
-                                   final Options initialOptions) {
46
-        super(activity, id);
47
-        this.componentName = componentName;
48
-        this.viewCreator = viewCreator;
49
-        this.options = initialOptions;
50
-    }
51
-
52
-    @RestrictTo(RestrictTo.Scope.TESTS)
53
-    TopBar getTopBar() {
54
-        return component.getTopBar();
55
-    }
56
-
57
-    @RestrictTo(RestrictTo.Scope.TESTS)
58
-    ReactComponent getComponent() {
59
-        return component;
60
-    }
61
-
62
-    @Override
63
-    public void destroy() {
64
-        super.destroy();
65
-        if (component != null) component.destroy();
66
-        component = null;
67
-    }
68
-
69
-    @Override
70
-    public void onViewAppeared() {
71
-        super.onViewAppeared();
72
-        ensureViewIsCreated();
73
-        component.applyOptions(options);
74
-        component.sendComponentStart();
75
-    }
76
-
77
-    @Override
78
-    public void onViewDisappear() {
79
-        super.onViewDisappear();
80
-        component.sendComponentStop();
81
-    }
82
-
83
-    @Override
84
-    protected boolean isViewShown() {
85
-        return super.isViewShown() && component.isReady();
86
-    }
87
-
88
-    @NonNull
89
-    @Override
90
-    protected View createView() {
91
-        component = (ReactComponent) viewCreator.create(getActivity(), getId(), componentName);
92
-        return component.asView();
93
-    }
94
-
95
-    @Override
96
-    public void mergeOptions(Options options) {
97
-        this.options.mergeWith(options);
98
-        component.applyOptions(this.options);
99
-    }
100
-
101
-    Options getOptions() {
102
-        return options;
103
-    }
104
-}

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

12
 import com.reactnativenavigation.presentation.NavigationOptionsListener;
12
 import com.reactnativenavigation.presentation.NavigationOptionsListener;
13
 import com.reactnativenavigation.presentation.OverlayPresenter;
13
 import com.reactnativenavigation.presentation.OverlayPresenter;
14
 import com.reactnativenavigation.utils.CompatUtils;
14
 import com.reactnativenavigation.utils.CompatUtils;
15
+import com.reactnativenavigation.utils.NoOpPromise;
15
 
16
 
16
 import java.util.Collection;
17
 import java.util.Collection;
17
 import java.util.Collections;
18
 import java.util.Collections;
18
 
19
 
19
 public class Navigator extends ParentController {
20
 public class Navigator extends ParentController {
20
 
21
 
21
-	private final ModalStack modalStack = new ModalStack();
22
+    private static final NoOpPromise NO_OP = new NoOpPromise();
23
+    private final ModalStack modalStack = new ModalStack();
22
 	private ViewController root;
24
 	private ViewController root;
23
 	private OverlayPresenter overlayPresenter;
25
 	private OverlayPresenter overlayPresenter;
24
     private Options defaultOptions = new Options();
26
     private Options defaultOptions = new Options();
27
 		super(activity, "navigator" + CompatUtils.generateViewId());
29
 		super(activity, "navigator" + CompatUtils.generateViewId());
28
 	}
30
 	}
29
 
31
 
30
-	@NonNull
32
+    @NonNull
31
 	@Override
33
 	@Override
32
 	protected ViewGroup createView() {
34
 	protected ViewGroup createView() {
33
 		return new FrameLayout(getActivity());
35
 		return new FrameLayout(getActivity());
46
 
48
 
47
 	@Override
49
 	@Override
48
 	public void destroy() {
50
 	public void destroy() {
49
-		modalStack.dismissAll(null);
51
+		modalStack.dismissAll(NO_OP);
50
 		super.destroy();
52
 		super.destroy();
51
 	}
53
 	}
52
 
54
 
53
-	/*
54
-	 * Navigation methods
55
-	 */
56
-
57
-	void setRoot(final ViewController viewController) {
58
-		setRoot(viewController, null);
59
-	}
60
-
61
 	public void setRoot(final ViewController viewController, Promise promise) {
55
 	public void setRoot(final ViewController viewController, Promise promise) {
62
 		if (root != null) {
56
 		if (root != null) {
63
 			root.destroy();
57
 			root.destroy();
65
 
59
 
66
 		root = viewController;
60
 		root = viewController;
67
 		getView().addView(viewController.getView());
61
 		getView().addView(viewController.getView());
68
-		if (promise != null) {
69
-			promise.resolve(viewController.getId());
70
-		}
62
+        promise.resolve(viewController.getId());
71
 	}
63
 	}
72
 
64
 
73
     public void setDefaultOptions(Options defaultOptions) {
65
     public void setDefaultOptions(Options defaultOptions) {
88
 		}
80
 		}
89
 	}
81
 	}
90
 
82
 
91
-	public void push(final String fromId, final ViewController viewController) {
92
-		push(fromId, viewController, null);
93
-	}
94
-
95
 	public void push(final String fromId, final ViewController viewController, Promise promise) {
83
 	public void push(final String fromId, final ViewController viewController, Promise promise) {
96
 		ViewController from = findControllerById(fromId);
84
 		ViewController from = findControllerById(fromId);
97
 		if (from != null) {
85
 		if (from != null) {
98
-		    from.performOnParentStack(stack -> stack.push(viewController, promise));
86
+		    from.performOnParentStack(stack -> stack.animatePush(viewController, promise));
99
 		}
87
 		}
100
 	}
88
 	}
101
 
89
 
102
-	void pop(final String fromId) {
103
-		pop(fromId, null);
104
-	}
105
-
106
 	void pop(final String fromId, Promise promise) {
90
 	void pop(final String fromId, Promise promise) {
107
 		ViewController from = findControllerById(fromId);
91
 		ViewController from = findControllerById(fromId);
108
 		if (from != null) {
92
 		if (from != null) {
110
 		}
94
 		}
111
 	}
95
 	}
112
 
96
 
113
-	void popSpecific(final String id) {
114
-		popSpecific(id, null);
115
-	}
116
-
117
 	public void popSpecific(final String id, Promise promise) {
97
 	public void popSpecific(final String id, Promise promise) {
118
 		ViewController from = findControllerById(id);
98
 		ViewController from = findControllerById(id);
119
 		if (from != null) {
99
 		if (from != null) {
123
 		}
103
 		}
124
 	}
104
 	}
125
 
105
 
126
-	void popToRoot(final String id) {
127
-		popToRoot(id, null);
128
-	}
129
-
130
 	public void popToRoot(final String id, Promise promise) {
106
 	public void popToRoot(final String id, Promise promise) {
131
 		ViewController from = findControllerById(id);
107
 		ViewController from = findControllerById(id);
132
 		if (from != null) {
108
 		if (from != null) {
134
 		}
110
 		}
135
 	}
111
 	}
136
 
112
 
137
-	void popTo(final String componentId) {
138
-		popTo(componentId, null);
139
-	}
140
-
141
 	public void popTo(final String componentId, Promise promise) {
113
 	public void popTo(final String componentId, Promise promise) {
142
 		ViewController target = findControllerById(componentId);
114
 		ViewController target = findControllerById(componentId);
143
 		if (target != null) {
115
 		if (target != null) {
170
 	}
142
 	}
171
 
143
 
172
 	static void rejectPromise(Promise promise) {
144
 	static void rejectPromise(Promise promise) {
173
-		if (promise != null) {
174
-			promise.reject(new Throwable("Nothing to pop"));
175
-		}
145
+        promise.reject(new Throwable("Nothing to pop"));
176
 	}
146
 	}
177
 
147
 
178
     @Nullable
148
     @Nullable

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

5
 import android.support.annotation.Nullable;
5
 import android.support.annotation.Nullable;
6
 import android.view.ViewGroup;
6
 import android.view.ViewGroup;
7
 
7
 
8
+import com.reactnativenavigation.parse.Options;
9
+import com.reactnativenavigation.views.ReactComponent;
10
+
8
 import java.util.Collection;
11
 import java.util.Collection;
9
 
12
 
10
-public abstract class ParentController extends ViewController {
13
+public abstract class ParentController<T extends ViewGroup> extends ViewController {
11
 
14
 
12
 	public ParentController(final Activity activity, final String id) {
15
 	public ParentController(final Activity activity, final String id) {
13
 		super(activity, id);
16
 		super(activity, id);
15
 
18
 
16
 	@NonNull
19
 	@NonNull
17
 	@Override
20
 	@Override
18
-	public ViewGroup getView() {
19
-		return (ViewGroup) super.getView();
21
+	public T getView() {
22
+		return (T) super.getView();
20
 	}
23
 	}
21
 
24
 
22
 	@NonNull
25
 	@NonNull
23
 	@Override
26
 	@Override
24
-	protected abstract ViewGroup createView();
27
+	protected abstract T createView();
25
 
28
 
26
-	@NonNull
29
+    @NonNull
27
 	public abstract Collection<? extends ViewController> getChildControllers();
30
 	public abstract Collection<? extends ViewController> getChildControllers();
28
 
31
 
29
 	@Nullable
32
 	@Nullable
40
 		return null;
43
 		return null;
41
 	}
44
 	}
42
 
45
 
46
+    public void applyOptions(Options options, ReactComponent childComponent) {
47
+
48
+    }
49
+
43
 	@Override
50
 	@Override
44
 	public void destroy() {
51
 	public void destroy() {
45
 		super.destroy();
52
 		super.destroy();
47
 			child.destroy();
54
 			child.destroy();
48
 		}
55
 		}
49
 	}
56
 	}
57
+
58
+    void clearOptions() {
59
+
60
+    }
50
 }
61
 }

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

2
 
2
 
3
 import android.app.Activity;
3
 import android.app.Activity;
4
 import android.support.annotation.NonNull;
4
 import android.support.annotation.NonNull;
5
+import android.support.annotation.RestrictTo;
5
 import android.view.View;
6
 import android.view.View;
6
-import android.view.ViewGroup;
7
-import android.widget.FrameLayout;
8
 
7
 
9
 import com.facebook.react.bridge.Promise;
8
 import com.facebook.react.bridge.Promise;
10
 import com.reactnativenavigation.anim.NavigationAnimator;
9
 import com.reactnativenavigation.anim.NavigationAnimator;
10
+import com.reactnativenavigation.parse.Options;
11
+import com.reactnativenavigation.utils.NoOpPromise;
12
+import com.reactnativenavigation.views.ReactComponent;
13
+import com.reactnativenavigation.views.StackLayout;
14
+import com.reactnativenavigation.views.TopBar;
11
 
15
 
12
 import java.util.Collection;
16
 import java.util.Collection;
13
 import java.util.Iterator;
17
 import java.util.Iterator;
14
 
18
 
15
-public class StackController extends ParentController {
19
+public class StackController extends ParentController <StackLayout> {
16
 
20
 
17
-	private final IdStack<ViewController> stack = new IdStack<>();
18
-	private final NavigationAnimator animator;
21
+    private static final NoOpPromise NO_OP = new NoOpPromise();
22
+    private final IdStack<ViewController> stack = new IdStack<>();
23
+    private final NavigationAnimator animator;
24
+    private StackLayout stackLayout;
19
 
25
 
20
-	public StackController(final Activity activity, String id) {
21
-		this(activity, id, new NavigationAnimator(activity));
22
-	}
23
-
24
-	public StackController(final Activity activity, String id, NavigationAnimator animator) {
26
+    public StackController(final Activity activity, String id) {
25
 		super(activity, id);
27
 		super(activity, id);
26
-		this.animator = animator;
27
-	}
28
+        animator = new NavigationAnimator(activity);
29
+    }
30
+
31
+    @RestrictTo(RestrictTo.Scope.TESTS)
32
+    TopBar getTopBar() {
33
+        return stackLayout.getTopBar();
34
+    }
28
 
35
 
29
-	public void push(final ViewController child, final Promise promise) {
30
-		final ViewController previousTop = peek();
36
+    @Override
37
+    public void applyOptions(Options options, ReactComponent component) {
38
+        stackLayout.applyOptions(options, component);
39
+    }
40
+
41
+    @Override
42
+    void clearOptions() {
43
+        stackLayout.clearOptions();
44
+    }
45
+
46
+    public void animatePush(final ViewController child, final Promise promise) {
47
+		final ViewController toRemove = stack.peek();
31
 
48
 
32
 		child.setParentController(this);
49
 		child.setParentController(this);
33
 		stack.push(child.getId(), child);
50
 		stack.push(child.getId(), child);
34
 		View enteringView = child.getView();
51
 		View enteringView = child.getView();
35
 		getView().addView(enteringView);
52
 		getView().addView(enteringView);
36
 
53
 
37
-		if (previousTop != null) {
38
-			animator.animatePush(enteringView, () -> {
39
-                getView().removeView(previousTop.getView());
40
-                if (promise != null) {
41
-                    promise.resolve(child.getId());
42
-                }
54
+		if (toRemove != null) {
55
+            animator.animatePush(enteringView, () -> {
56
+                getView().removeView(toRemove.getView());
57
+                promise.resolve(child.getId());
43
             });
58
             });
44
-		} else if (promise != null) {
59
+		} else {
45
 			promise.resolve(child.getId());
60
 			promise.resolve(child.getId());
46
 		}
61
 		}
47
 	}
62
 	}
48
 
63
 
49
-	boolean canPop() {
50
-		return stack.size() > 1;
51
-	}
64
+    public void pop(final Promise promise) {
65
+        if (!canPop()) {
66
+            Navigator.rejectPromise(promise);
67
+            return;
68
+        }
52
 
69
 
53
-	void pop(Promise promise) {
54
-		pop(true, promise);
55
-	}
70
+        final ViewController poppedTop = stack.pop();
71
+        ViewController newTop = stack.peek();
72
+
73
+        View enteringView = newTop.getView();
74
+        final View exitingView = poppedTop.getView();
75
+        getView().addView(enteringView, getView().getChildCount() - 1);
56
 
76
 
57
-	private void pop(boolean animate, final Promise promise) {
77
+        finishPopping(exitingView, poppedTop, promise);
78
+    }
79
+
80
+	public void animatePop(final Promise promise) {
58
 		if (!canPop()) {
81
 		if (!canPop()) {
59
 			Navigator.rejectPromise(promise);
82
 			Navigator.rejectPromise(promise);
60
 			return;
83
 			return;
61
 		}
84
 		}
62
 
85
 
63
 		final ViewController poppedTop = stack.pop();
86
 		final ViewController poppedTop = stack.pop();
64
-		ViewController newTop = peek();
87
+		ViewController newTop = stack.peek();
65
 
88
 
66
 		View enteringView = newTop.getView();
89
 		View enteringView = newTop.getView();
67
 		final View exitingView = poppedTop.getView();
90
 		final View exitingView = poppedTop.getView();
68
 		getView().addView(enteringView, getView().getChildCount() - 1);
91
 		getView().addView(enteringView, getView().getChildCount() - 1);
69
 
92
 
70
-		if (animate) {
71
-			animator.animatePop(exitingView, () -> finishPopping(exitingView, poppedTop, promise));
72
-		} else {
73
-			finishPopping(exitingView, poppedTop, promise);
74
-		}
93
+        animator.animatePop(exitingView, () -> finishPopping(exitingView, poppedTop, promise));
75
 	}
94
 	}
76
 
95
 
96
+    boolean canPop() {
97
+        return stack.size() > 1;
98
+    }
99
+
77
 	private void finishPopping(View exitingView, ViewController poppedTop, Promise promise) {
100
 	private void finishPopping(View exitingView, ViewController poppedTop, Promise promise) {
78
 		getView().removeView(exitingView);
101
 		getView().removeView(exitingView);
79
 		poppedTop.destroy();
102
 		poppedTop.destroy();
80
-		if (promise != null) {
81
-			promise.resolve(poppedTop.getId());
82
-		}
83
-	}
84
-
85
-	void popSpecific(final ViewController childController) {
86
-		popSpecific(childController, null);
103
+        promise.resolve(poppedTop.getId());
87
 	}
104
 	}
88
 
105
 
89
 	void popSpecific(final ViewController childController, Promise promise) {
106
 	void popSpecific(final ViewController childController, Promise promise) {
90
 		if (stack.isTop(childController.getId())) {
107
 		if (stack.isTop(childController.getId())) {
91
-			pop(promise);
108
+			animatePop(promise);
92
 		} else {
109
 		} else {
93
 			stack.remove(childController.getId());
110
 			stack.remove(childController.getId());
94
 			childController.destroy();
111
 			childController.destroy();
95
-			if (promise != null) {
96
-				promise.resolve(childController.getId());
97
-			}
112
+            promise.resolve(childController.getId());
98
 		}
113
 		}
99
 	}
114
 	}
100
 
115
 
101
-	void popTo(ViewController viewController) {
102
-		popTo(viewController, null);
103
-	}
104
-
105
 	void popTo(final ViewController viewController, Promise promise) {
116
 	void popTo(final ViewController viewController, Promise promise) {
106
 		if (!stack.containsId(viewController.getId())) {
117
 		if (!stack.containsId(viewController.getId())) {
107
 			Navigator.rejectPromise(promise);
118
 			Navigator.rejectPromise(promise);
113
 		while (!viewController.getId().equals(currentControlId)) {
124
 		while (!viewController.getId().equals(currentControlId)) {
114
 			String nextControlId = iterator.next();
125
 			String nextControlId = iterator.next();
115
 			boolean animate = nextControlId.equals(viewController.getId());
126
 			boolean animate = nextControlId.equals(viewController.getId());
116
-			pop(animate, animate ? promise : null);
127
+			if (animate) {
128
+			    animatePop(promise);
129
+            } else {
130
+			    pop(NO_OP);
131
+            }
117
 			currentControlId = nextControlId;
132
 			currentControlId = nextControlId;
118
 		}
133
 		}
119
 	}
134
 	}
120
 
135
 
121
-	void popToRoot() {
122
-		popToRoot(null);
123
-	}
124
-
125
 	void popToRoot(Promise promise) {
136
 	void popToRoot(Promise promise) {
126
 		while (canPop()) {
137
 		while (canPop()) {
127
-			boolean animate = stack.size() == 2; //first element is root
128
-			pop(animate, animate ? promise : null);
138
+			boolean animate = stack.size() == 2; // First element is root
139
+            if (animate) {
140
+                animatePop(promise);
141
+            } else {
142
+                pop(NO_OP);
143
+            }
129
 		}
144
 		}
130
 	}
145
 	}
131
 
146
 
144
 	@Override
159
 	@Override
145
 	public boolean handleBack() {
160
 	public boolean handleBack() {
146
 		if (canPop()) {
161
 		if (canPop()) {
147
-			pop(null);
162
+			animatePop(NO_OP);
148
 			return true;
163
 			return true;
149
-		} else {
150
-			return false;
151
 		}
164
 		}
165
+        return false;
152
 	}
166
 	}
153
 
167
 
154
-	@NonNull
155
-	@Override
156
-	protected ViewGroup createView() {
157
-		return new FrameLayout(getActivity());
158
-	}
168
+    @NonNull
169
+    @Override
170
+    protected StackLayout createView() {
171
+        stackLayout = new StackLayout(getActivity());
172
+        return stackLayout;
173
+    }
159
 
174
 
160
 	@NonNull
175
 	@NonNull
161
 	@Override
176
 	@Override

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

16
 
16
 
17
 public abstract class ViewController implements ViewTreeObserver.OnGlobalLayoutListener {
17
 public abstract class ViewController implements ViewTreeObserver.OnGlobalLayoutListener {
18
 
18
 
19
+    public Options options;
20
+
19
 	private final Activity activity;
21
 	private final Activity activity;
20
 	private final String id;
22
 	private final String id;
21
-
22
 	private View view;
23
 	private View view;
23
 	private ParentController parentController;
24
 	private ParentController parentController;
24
 	private boolean isShown = false;
25
 	private boolean isShown = false;
25
-
26
     private boolean isDestroyed;
26
     private boolean isDestroyed;
27
 
27
 
28
 	public ViewController(Activity activity, String id) {
28
 	public ViewController(Activity activity, String id) {
32
 
32
 
33
 	protected abstract View createView();
33
 	protected abstract View createView();
34
 
34
 
35
-	@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
35
+	@SuppressWarnings("WeakerAccess")
36
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
36
 	public void ensureViewIsCreated() {
37
 	public void ensureViewIsCreated() {
37
 		getView();
38
 		getView();
38
 	}
39
 	}
41
 		return false;
42
 		return false;
42
 	}
43
 	}
43
 
44
 
45
+    public void applyOptions(Options options) {
46
+
47
+    }
48
+
44
 	public Activity getActivity() {
49
 	public Activity getActivity() {
45
 		return activity;
50
 		return activity;
46
 	}
51
 	}
47
 
52
 
48
-    protected ViewController getParentController() {
49
-	    return parentController;
53
+	protected void applyOnParentStack(Task<ParentController> task) {
54
+        if (parentController != null) {
55
+            task.run(parentController);
56
+        }
50
     }
57
     }
51
 
58
 
52
 	@Nullable
59
 	@Nullable
106
 
113
 
107
 	public void onViewDisappear() {
114
 	public void onViewDisappear() {
108
         isShown = false;
115
         isShown = false;
109
-    }
110
-
111
-    public void applyOptions(Options options) {
112
-
113
     }
116
     }
114
 
117
 
115
 	public void destroy() {
118
 	public void destroy() {

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

13
 
13
 
14
     private final String componentName;
14
     private final String componentName;
15
     private ComponentViewController.ReactViewCreator viewCreator;
15
     private ComponentViewController.ReactViewCreator viewCreator;
16
-    private final Options options;
17
     private TopTab topTab;
16
     private TopTab topTab;
18
     private boolean isSelectedTab;
17
     private boolean isSelectedTab;
19
 
18
 
34
 
33
 
35
     @Override
34
     @Override
36
     public void applyOptions(Options options) {
35
     public void applyOptions(Options options) {
37
-        getParentController().applyOptions(options);
36
+        applyOnParentStack(parentController -> parentController.applyOptions(options));
38
     }
37
     }
39
 
38
 
40
     @Override
39
     @Override
72
     }
71
     }
73
 
72
 
74
     String getTabTitle() {
73
     String getTabTitle() {
75
-        return options.topTabOptions.title;
74
+        return options.topTabOptions.title.get("");
76
     }
75
     }
77
 
76
 
78
     public void setTabIndex(int i) {
77
     public void setTabIndex(int i) {

+ 8
- 5
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsAdapter.java View File

5
 import android.view.View;
5
 import android.view.View;
6
 import android.view.ViewGroup;
6
 import android.view.ViewGroup;
7
 
7
 
8
+import com.reactnativenavigation.parse.Options;
9
+import com.reactnativenavigation.viewcontrollers.ViewController;
10
+
8
 import java.util.List;
11
 import java.util.List;
9
 
12
 
10
 public class TopTabsAdapter extends PagerAdapter implements ViewPager.OnPageChangeListener {
13
 public class TopTabsAdapter extends PagerAdapter implements ViewPager.OnPageChangeListener {
11
-    private List<TopTabController> tabs;
14
+    private List<ViewController> tabs;
12
     private int currentPage = 0;
15
     private int currentPage = 0;
13
 
16
 
14
-    public TopTabsAdapter(List<TopTabController> tabs) {
17
+    public TopTabsAdapter(List<ViewController> tabs) {
15
         this.tabs = tabs;
18
         this.tabs = tabs;
16
     }
19
     }
17
 
20
 
18
     @Override
21
     @Override
19
     public CharSequence getPageTitle(int position) {
22
     public CharSequence getPageTitle(int position) {
20
-        return tabs.get(position).getTabTitle();
23
+        return getTabOptions(position).topTabOptions.title.get("");
21
     }
24
     }
22
 
25
 
23
     @Override
26
     @Override
52
 
55
 
53
     }
56
     }
54
 
57
 
55
-    int getCurrentItem() {
56
-        return currentPage;
58
+    private Options getTabOptions(int position) {
59
+        return tabs.get(position).options;
57
     }
60
     }
58
 }
61
 }

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

17
 
17
 
18
 public class TopTabsController extends ParentController implements NavigationOptionsListener {
18
 public class TopTabsController extends ParentController implements NavigationOptionsListener {
19
 
19
 
20
-    private List<TopTabController> tabs;
20
+    private List<ViewController> tabs;
21
     private TopTabsLayout topTabsLayout;
21
     private TopTabsLayout topTabsLayout;
22
     private TopTabsLayoutCreator viewCreator;
22
     private TopTabsLayoutCreator viewCreator;
23
     private Options options;
23
     private Options options;
24
 
24
 
25
-    public TopTabsController(Activity activity, String id, List<TopTabController> tabs, TopTabsLayoutCreator viewCreator, Options options) {
25
+    public TopTabsController(Activity activity, String id, List<ViewController> tabs, TopTabsLayoutCreator viewCreator, Options options) {
26
         super(activity, id);
26
         super(activity, id);
27
         this.viewCreator = viewCreator;
27
         this.viewCreator = viewCreator;
28
         this.options = options;
28
         this.options = options;
48
     @Override
48
     @Override
49
     public void onViewAppeared() {
49
     public void onViewAppeared() {
50
         applyOptions(options);
50
         applyOptions(options);
51
-        performOnCurrentTab(TopTabController::onViewAppeared);
51
+        performOnCurrentTab(ViewController::onViewAppeared);
52
     }
52
     }
53
 
53
 
54
     @Override
54
     @Override
55
     public void onViewDisappear() {
55
     public void onViewDisappear() {
56
-        performOnCurrentTab(TopTabController::onViewDisappear);
56
+        performOnCurrentTab(ViewController::onViewDisappear);
57
     }
57
     }
58
 
58
 
59
     @Override
59
     @Override
70
         topTabsLayout.switchToTab(index);
70
         topTabsLayout.switchToTab(index);
71
     }
71
     }
72
 
72
 
73
-    private void performOnCurrentTab(Task<TopTabController> task) {
73
+    private void performOnCurrentTab(Task<ViewController> task) {
74
         task.run(tabs.get(topTabsLayout.getCurrentItem()));
74
         task.run(tabs.get(topTabsLayout.getCurrentItem()));
75
     }
75
     }
76
 }
76
 }

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

15
 @SuppressLint("ViewConstructor")
15
 @SuppressLint("ViewConstructor")
16
 public class TopTabsViewPager extends ViewPager {
16
 public class TopTabsViewPager extends ViewPager {
17
     private static final int OFFSCREEN_PAGE_LIMIT = 99;
17
     private static final int OFFSCREEN_PAGE_LIMIT = 99;
18
-    private List<TopTabController> tabs;
18
+    private List<ViewController> tabs;
19
 
19
 
20
-    public TopTabsViewPager(Context context, List<TopTabController> tabs, TopTabsAdapter adapter) {
20
+    public TopTabsViewPager(Context context, List<ViewController> tabs, TopTabsAdapter adapter) {
21
         super(context);
21
         super(context);
22
         this.tabs = tabs;
22
         this.tabs = tabs;
23
         init(adapter);
23
         init(adapter);

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

5
 public interface Component {
5
 public interface Component {
6
     void applyOptions(Options options);
6
     void applyOptions(Options options);
7
 
7
 
8
-    TopBar getTopBar();
9
-
10
     void drawBehindTopBar();
8
     void drawBehindTopBar();
11
 
9
 
12
-    void drawBelowTopBar();
10
+    void drawBelowTopBar(TopBar topBar);
13
 }
11
 }

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

3
 import android.annotation.SuppressLint;
3
 import android.annotation.SuppressLint;
4
 import android.content.Context;
4
 import android.content.Context;
5
 import android.view.View;
5
 import android.view.View;
6
+import android.widget.FrameLayout;
6
 import android.widget.RelativeLayout;
7
 import android.widget.RelativeLayout;
7
 
8
 
8
 import com.reactnativenavigation.interfaces.ScrollEventListener;
9
 import com.reactnativenavigation.interfaces.ScrollEventListener;
9
 import com.reactnativenavigation.parse.Options;
10
 import com.reactnativenavigation.parse.Options;
10
-import com.reactnativenavigation.presentation.OptionsPresenter;
11
 import com.reactnativenavigation.viewcontrollers.ComponentViewController.IReactView;
11
 import com.reactnativenavigation.viewcontrollers.ComponentViewController.IReactView;
12
 
12
 
13
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
13
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
14
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
14
+import static android.widget.RelativeLayout.BELOW;
15
 
15
 
16
 @SuppressLint("ViewConstructor")
16
 @SuppressLint("ViewConstructor")
17
-public class ComponentLayout extends RelativeLayout implements ReactComponent, TitleBarButton.OnClickListener {
17
+public class ComponentLayout extends FrameLayout implements ReactComponent, TitleBarButton.OnClickListener {
18
 
18
 
19
-    private TopBar topBar;
20
     private IReactView reactView;
19
     private IReactView reactView;
21
-    private final OptionsPresenter optionsPresenter;
22
 
20
 
23
 	public ComponentLayout(Context context, IReactView reactView) {
21
 	public ComponentLayout(Context context, IReactView reactView) {
24
 		super(context);
22
 		super(context);
25
 		this.reactView = reactView;
23
 		this.reactView = reactView;
26
-		this.topBar = new TopBar(context, reactView.asView(), reactView.getScrollEventListener(), this);
27
-        optionsPresenter = new OptionsPresenter(this);
28
-        initViews();
29
-    }
30
-
31
-    private void initViews() {
32
-        LayoutParams layoutParams = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
33
-        layoutParams.addRule(BELOW, topBar.getId());
34
-        addView(reactView.asView(), layoutParams);
35
-        addView(topBar, new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
24
+        addView(reactView.asView(), MATCH_PARENT, MATCH_PARENT);
36
     }
25
     }
37
 
26
 
38
     @Override
27
     @Override
62
 
51
 
63
     @Override
52
     @Override
64
     public void applyOptions(Options options) {
53
     public void applyOptions(Options options) {
65
-        optionsPresenter.applyOptions(options);
54
+
66
     }
55
     }
67
 
56
 
68
     @Override
57
     @Override
75
         return reactView.getScrollEventListener();
64
         return reactView.getScrollEventListener();
76
     }
65
     }
77
 
66
 
78
-    @Override
79
-    public TopBar getTopBar() {
80
-        return topBar;
81
-    }
82
-
83
     @Override
67
     @Override
84
     public void drawBehindTopBar() {
68
     public void drawBehindTopBar() {
85
         RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) reactView.asView().getLayoutParams();
69
         RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) reactView.asView().getLayoutParams();
88
     }
72
     }
89
 
73
 
90
     @Override
74
     @Override
91
-    public void drawBelowTopBar() {
75
+    public void drawBelowTopBar(TopBar topBar) {
92
         RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) reactView.asView().getLayoutParams();
76
         RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) reactView.asView().getLayoutParams();
93
         layoutParams.addRule(BELOW, topBar.getId());
77
         layoutParams.addRule(BELOW, topBar.getId());
94
         reactView.asView().setLayoutParams(layoutParams);
78
         reactView.asView().setLayoutParams(layoutParams);

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

1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+import android.support.annotation.RestrictTo;
5
+import android.widget.RelativeLayout;
6
+
7
+import com.reactnativenavigation.parse.Options;
8
+import com.reactnativenavigation.presentation.OptionsPresenter;
9
+import com.reactnativenavigation.utils.CompatUtils;
10
+
11
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
12
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
13
+
14
+public class StackLayout extends RelativeLayout implements TitleBarButton.OnClickListener {
15
+
16
+    private final TopBar topBar;
17
+
18
+    public StackLayout(Context context) {
19
+        super(context);
20
+        topBar = new TopBar(context, this);
21
+        topBar.setId(CompatUtils.generateViewId());
22
+        createLayout();
23
+    }
24
+
25
+    void createLayout() {
26
+        addView(topBar, MATCH_PARENT, WRAP_CONTENT);
27
+    }
28
+
29
+    @Override
30
+    public void onPress(String buttonId) {
31
+
32
+    }
33
+
34
+    public void applyOptions(Options options, ReactComponent component) {
35
+        new OptionsPresenter(topBar, component).applyOptions(options);
36
+    }
37
+
38
+    @RestrictTo(RestrictTo.Scope.TESTS)
39
+    public TopBar getTopBar() {
40
+        return topBar;
41
+    }
42
+
43
+    public void clearOptions() {
44
+        topBar.clear();
45
+    }
46
+}

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

38
 	}
38
 	}
39
 
39
 
40
 	void addToMenu(Context context, final Menu menu) {
40
 	void addToMenu(Context context, final Menu menu) {
41
-		MenuItem menuItem = menu.add(button.title);
41
+		MenuItem menuItem = menu.add(button.title.get(""));
42
 		menuItem.setShowAsAction(button.showAsAction);
42
 		menuItem.setShowAsAction(button.showAsAction);
43
 		menuItem.setEnabled(button.disabled != Options.BooleanOptions.True);
43
 		menuItem.setEnabled(button.disabled != Options.BooleanOptions.True);
44
 		menuItem.setOnMenuItemClickListener(this);
44
 		menuItem.setOnMenuItemClickListener(this);
57
 			return;
57
 			return;
58
 		}
58
 		}
59
 
59
 
60
-		ImageUtils.tryLoadIcon(context, button.icon, new ImageUtils.ImageLoadingListener() {
60
+		ImageUtils.tryLoadIcon(context, button.icon.get(), new ImageUtils.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;
77
 	}
77
 	}
78
 
78
 
79
 	private void applyIcon(Context context, final MenuItem menuItem) {
79
 	private void applyIcon(Context context, final MenuItem menuItem) {
80
-		ImageUtils.tryLoadIcon(context, button.icon, new ImageUtils.ImageLoadingListener() {
80
+		ImageUtils.tryLoadIcon(context, button.icon.get(), new ImageUtils.ImageLoadingListener() {
81
 			@Override
81
 			@Override
82
 			public void onComplete(@NonNull Drawable drawable) {
82
 			public void onComplete(@NonNull Drawable drawable) {
83
 				icon = drawable;
83
 				icon = drawable;
116
 	}
116
 	}
117
 
117
 
118
 	private void setFontSize(MenuItem menuItem) {
118
 	private void setFontSize(MenuItem menuItem) {
119
-		SpannableString spanString = new SpannableString(button.title);
120
-		spanString.setSpan(new AbsoluteSizeSpan(button.buttonFontSize, true), 0, button.title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
119
+		SpannableString spanString = new SpannableString(button.title.get());
120
+		spanString.setSpan(new AbsoluteSizeSpan(button.buttonFontSize, true), 0, button.title.get().length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
121
 		menuItem.setTitleCondensed(spanString);
121
 		menuItem.setTitleCondensed(spanString);
122
 	}
122
 	}
123
 
123
 
134
 	@NonNull
134
 	@NonNull
135
 	private ArrayList<View> findActualTextViewInMenuByLabel() {
135
 	private ArrayList<View> findActualTextViewInMenuByLabel() {
136
 		ArrayList<View> outViews = new ArrayList<>();
136
 		ArrayList<View> outViews = new ArrayList<>();
137
-		this.toolbar.findViewsWithText(outViews, button.title, View.FIND_VIEWS_WITH_TEXT);
137
+		this.toolbar.findViewsWithText(outViews, button.title.get(), View.FIND_VIEWS_WITH_TEXT);
138
 		return outViews;
138
 		return outViews;
139
 	}
139
 	}
140
 
140
 

+ 22
- 16
lib/android/app/src/main/java/com/reactnativenavigation/views/TopBar.java View File

3
 import android.annotation.SuppressLint;
3
 import android.annotation.SuppressLint;
4
 import android.content.Context;
4
 import android.content.Context;
5
 import android.graphics.Typeface;
5
 import android.graphics.Typeface;
6
-import android.support.annotation.ColorInt;
7
 import android.support.annotation.Nullable;
6
 import android.support.annotation.Nullable;
8
 import android.support.design.widget.AppBarLayout;
7
 import android.support.design.widget.AppBarLayout;
9
 import android.support.v7.widget.Toolbar;
8
 import android.support.v7.widget.Toolbar;
18
 import com.reactnativenavigation.interfaces.ScrollEventListener;
17
 import com.reactnativenavigation.interfaces.ScrollEventListener;
19
 import com.reactnativenavigation.parse.Button;
18
 import com.reactnativenavigation.parse.Button;
20
 import com.reactnativenavigation.parse.Color;
19
 import com.reactnativenavigation.parse.Color;
20
+import com.reactnativenavigation.parse.Fraction;
21
 import com.reactnativenavigation.parse.Number;
21
 import com.reactnativenavigation.parse.Number;
22
 import com.reactnativenavigation.parse.Options;
22
 import com.reactnativenavigation.parse.Options;
23
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsViewPager;
23
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsViewPager;
32
     private final TopBarAnimator animator;
32
     private final TopBarAnimator animator;
33
     private TopTabs topTabs;
33
     private TopTabs topTabs;
34
 
34
 
35
-    public TopBar(final Context context, View contentView, ScrollEventListener scrollEventListener, TitleBarButton.OnClickListener onClickListener) {
35
+    public TopBar(final Context context, TitleBarButton.OnClickListener onClickListener) {
36
         super(context);
36
         super(context);
37
         this.onClickListener = onClickListener;
37
         this.onClickListener = onClickListener;
38
-        collapsingBehavior = new TopBarCollapseBehavior(this, scrollEventListener);
38
+        collapsingBehavior = new TopBarCollapseBehavior(this);
39
         titleBar = new Toolbar(context);
39
         titleBar = new Toolbar(context);
40
         topTabs = new TopTabs(getContext());
40
         topTabs = new TopTabs(getContext());
41
-        this.animator = new TopBarAnimator(this, contentView);
41
+        this.animator = new TopBarAnimator(this);
42
         addView(titleBar);
42
         addView(titleBar);
43
     }
43
     }
44
 
44
 
50
         return titleBar.getTitle() != null ? titleBar.getTitle().toString() : "";
50
         return titleBar.getTitle() != null ? titleBar.getTitle().toString() : "";
51
     }
51
     }
52
 
52
 
53
-    public void setTitleTextColor(@ColorInt int color) {
54
-        titleBar.setTitleTextColor(color);
53
+    public void setTitleTextColor(Color color) {
54
+        if (color.hasValue()) titleBar.setTitleTextColor(color.get());
55
     }
55
     }
56
 
56
 
57
-    public void setTitleFontSize(float size) {
57
+    public void setTitleFontSize(Fraction size) {
58
         TextView titleTextView = getTitleTextView();
58
         TextView titleTextView = getTitleTextView();
59
-        if (titleTextView != null) {
60
-            titleTextView.setTextSize(size);
59
+        if (titleTextView != null && size.hasValue()) {
60
+            titleTextView.setTextSize(size.get());
61
         }
61
         }
62
     }
62
     }
63
 
63
 
89
         return findTextView(titleBar);
89
         return findTextView(titleBar);
90
     }
90
     }
91
 
91
 
92
-    @Override
93
-    public void setBackgroundColor(@ColorInt int color) {
94
-        titleBar.setBackgroundColor(color);
92
+    public void setBackgroundColor(Color color) {
93
+        if (color.hasValue()) titleBar.setBackgroundColor(color.get());
95
     }
94
     }
96
 
95
 
97
     @Nullable
96
     @Nullable
98
     private TextView findTextView(ViewGroup root) {
97
     private TextView findTextView(ViewGroup root) {
99
         for (int i = 0; i < root.getChildCount(); i++) {
98
         for (int i = 0; i < root.getChildCount(); i++) {
100
             View view = root.getChildAt(i);
99
             View view = root.getChildAt(i);
100
+            if (view instanceof ViewGroup) {
101
+                view = findTextView((ViewGroup) view);
102
+            }
101
             if (view instanceof TextView) {
103
             if (view instanceof TextView) {
102
                 return (TextView) view;
104
                 return (TextView) view;
103
             }
105
             }
104
-            if (view instanceof ViewGroup) {
105
-                return findTextView((ViewGroup) view);
106
-            }
107
         }
106
         }
108
         return null;
107
         return null;
109
     }
108
     }
110
 
109
 
111
     private void setLeftButtons(ArrayList<Button> leftButtons) {
110
     private void setLeftButtons(ArrayList<Button> leftButtons) {
112
-        if (leftButtons == null || leftButtons.isEmpty()) {
111
+        if (leftButtons == null) return;
112
+        if (leftButtons.isEmpty()) {
113
             titleBar.setNavigationIcon(null);
113
             titleBar.setNavigationIcon(null);
114
             return;
114
             return;
115
         }
115
         }
185
             setVisibility(View.GONE);
185
             setVisibility(View.GONE);
186
         }
186
         }
187
     }
187
     }
188
+
189
+    public void clear() {
190
+        titleBar.setTitle(null);
191
+        titleBar.setNavigationIcon(null);
192
+        titleBar.getMenu().clear();
193
+    }
188
 }
194
 }

+ 16
- 28
lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsLayout.java View File

1
 package com.reactnativenavigation.views;
1
 package com.reactnativenavigation.views;
2
 
2
 
3
-import android.annotation.*;
4
-import android.content.*;
5
-import android.support.annotation.*;
6
-import android.support.v4.view.*;
7
-import android.view.*;
8
-import android.widget.*;
3
+import android.annotation.SuppressLint;
4
+import android.content.Context;
5
+import android.support.annotation.RestrictTo;
6
+import android.support.v4.view.ViewPager;
7
+import android.view.View;
8
+import android.view.ViewGroup;
9
+import android.widget.RelativeLayout;
9
 
10
 
10
-import com.reactnativenavigation.parse.*;
11
-import com.reactnativenavigation.presentation.*;
12
-import com.reactnativenavigation.utils.*;
13
-import com.reactnativenavigation.viewcontrollers.toptabs.*;
11
+import com.reactnativenavigation.parse.Options;
12
+import com.reactnativenavigation.viewcontrollers.ViewController;
13
+import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsAdapter;
14
+import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsViewPager;
14
 
15
 
15
-import java.util.*;
16
+import java.util.List;
16
 
17
 
17
 @SuppressLint("ViewConstructor")
18
 @SuppressLint("ViewConstructor")
18
 public class TopTabsLayout extends RelativeLayout implements Component, TitleBarButton.OnClickListener {
19
 public class TopTabsLayout extends RelativeLayout implements Component, TitleBarButton.OnClickListener {
19
 
20
 
20
     private TopBar topBar;
21
     private TopBar topBar;
21
-    private List<TopTabController> tabs;
22
     private TopTabsViewPager viewPager;
22
     private TopTabsViewPager viewPager;
23
-    private final OptionsPresenter optionsPresenter;
24
 
23
 
25
-    public TopTabsLayout(Context context, List<TopTabController> tabs, TopTabsAdapter adapter) {
24
+    public TopTabsLayout(Context context, List<ViewController> tabs, TopTabsAdapter adapter) {
26
         super(context);
25
         super(context);
27
-        this.tabs = tabs;
28
         viewPager = new TopTabsViewPager(context, tabs, adapter);
26
         viewPager = new TopTabsViewPager(context, tabs, adapter);
29
-        topBar = new TopBar(context, viewPager, null, this);
27
+        topBar = new TopBar(context, this);
30
         topBar.setId(View.generateViewId());
28
         topBar.setId(View.generateViewId());
31
-        optionsPresenter = new OptionsPresenter(this);
29
+
32
         initViews();
30
         initViews();
33
     }
31
     }
34
 
32
 
42
 
40
 
43
     @Override
41
     @Override
44
     public void applyOptions(Options options) {
42
     public void applyOptions(Options options) {
45
-        optionsPresenter.applyOptions(options);
46
-    }
47
 
43
 
48
-    @Override
49
-    public TopBar getTopBar() {
50
-        return topBar;
51
     }
44
     }
52
 
45
 
53
     @Override
46
     @Override
56
     }
49
     }
57
 
50
 
58
     @Override
51
     @Override
59
-    public void drawBelowTopBar() {
52
+    public void drawBelowTopBar(TopBar topBar) {
60
 
53
 
61
     }
54
     }
62
 
55
 
65
         return viewPager;
58
         return viewPager;
66
     }
59
     }
67
 
60
 
68
-
69
-    public void performOnCurrentTab(Task<TopTabController> task) {
70
-        task.run(tabs.get(viewPager.getCurrentItem()));
71
-    }
72
-
73
     public void switchToTab(int index) {
61
     public void switchToTab(int index) {
74
         viewPager.setCurrentItem(index);
62
         viewPager.setCurrentItem(index);
75
     }
63
     }

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

2
 
2
 
3
 import android.content.Context;
3
 import android.content.Context;
4
 
4
 
5
-import com.reactnativenavigation.viewcontrollers.toptabs.TopTabController;
5
+import com.reactnativenavigation.viewcontrollers.ViewController;
6
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsAdapter;
6
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsAdapter;
7
 
7
 
8
 import java.util.List;
8
 import java.util.List;
9
 
9
 
10
 public class TopTabsLayoutCreator {
10
 public class TopTabsLayoutCreator {
11
     private Context context;
11
     private Context context;
12
-    private List<TopTabController> tabs;
12
+    private List<ViewController> tabs;
13
 
13
 
14
-    public TopTabsLayoutCreator(Context context, List<TopTabController> tabs) {
14
+    public TopTabsLayoutCreator(Context context, List<ViewController> tabs) {
15
         this.context = context;
15
         this.context = context;
16
         this.tabs = tabs;
16
         this.tabs = tabs;
17
     }
17
     }

+ 10
- 17
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestComponentLayout.java View File

1
 package com.reactnativenavigation.mocks;
1
 package com.reactnativenavigation.mocks;
2
 
2
 
3
-import android.content.*;
4
-import android.view.*;
5
-import android.widget.*;
3
+import android.content.Context;
4
+import android.view.View;
5
+import android.view.ViewGroup;
6
+import android.widget.RelativeLayout;
6
 
7
 
7
 import com.reactnativenavigation.interfaces.ScrollEventListener;
8
 import com.reactnativenavigation.interfaces.ScrollEventListener;
8
-import com.reactnativenavigation.parse.*;
9
-import com.reactnativenavigation.presentation.*;
10
-import com.reactnativenavigation.views.*;
9
+import com.reactnativenavigation.parse.Options;
10
+import com.reactnativenavigation.views.ReactComponent;
11
+import com.reactnativenavigation.views.TitleBarButton;
12
+import com.reactnativenavigation.views.TopBar;
11
 
13
 
12
 public class TestComponentLayout extends RelativeLayout implements ReactComponent, TitleBarButton.OnClickListener {
14
 public class TestComponentLayout extends RelativeLayout implements ReactComponent, TitleBarButton.OnClickListener {
13
 
15
 
14
-    private final TopBar topBar;
15
     private final View contentView;
16
     private final View contentView;
16
-    private final OptionsPresenter optionsPresenter;
17
 
17
 
18
     public TestComponentLayout(final Context context) {
18
     public TestComponentLayout(final Context context) {
19
         super(context);
19
         super(context);
20
         contentView = new View(context);
20
         contentView = new View(context);
21
-        topBar = new TopBar(context, contentView, null, this);
22
-        addView(topBar);
23
         addView(contentView);
21
         addView(contentView);
24
-        optionsPresenter = new OptionsPresenter(this);
25
-    }
26
-
27
-    public TopBar getTopBar() {
28
-        return topBar;
29
     }
22
     }
30
 
23
 
31
     @Override
24
     @Override
36
     }
29
     }
37
 
30
 
38
     @Override
31
     @Override
39
-    public void drawBelowTopBar() {
32
+    public void drawBelowTopBar(TopBar topBar) {
40
         RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
33
         RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
41
         layoutParams.addRule(BELOW, topBar.getId());
34
         layoutParams.addRule(BELOW, topBar.getId());
42
         contentView.setLayoutParams(layoutParams);
35
         contentView.setLayoutParams(layoutParams);
66
 
59
 
67
     @Override
60
     @Override
68
     public void applyOptions(Options options) {
61
     public void applyOptions(Options options) {
69
-        optionsPresenter.applyOptions(options);
62
+
70
     }
63
     }
71
 
64
 
72
     @Override
65
     @Override

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

53
     }
53
     }
54
 
54
 
55
     private void assertResult(Options result) {
55
     private void assertResult(Options result) {
56
-        assertThat(result.topBarOptions.title).isEqualTo(TITLE);
57
-        assertThat(result.topBarOptions.backgroundColor).isEqualTo(TOP_BAR_BACKGROUND_COLOR);
58
-        assertThat(result.topBarOptions.textColor).isEqualTo(TOP_BAR_TEXT_COLOR);
59
-        assertThat(result.topBarOptions.textFontSize).isEqualTo(TOP_BAR_FONT_SIZE);
56
+        assertThat(result.topBarOptions.title.get()).isEqualTo(TITLE);
57
+        assertThat(result.topBarOptions.backgroundColor.get()).isEqualTo(TOP_BAR_BACKGROUND_COLOR);
58
+        assertThat(result.topBarOptions.textColor.get()).isEqualTo(TOP_BAR_TEXT_COLOR);
59
+        assertThat(result.topBarOptions.textFontSize.get()).isEqualTo(TOP_BAR_FONT_SIZE);
60
         assertThat(result.topBarOptions.textFontFamily).isEqualTo(TOP_BAR_TYPEFACE);
60
         assertThat(result.topBarOptions.textFontFamily).isEqualTo(TOP_BAR_TYPEFACE);
61
         assertThat(result.topBarOptions.hidden).isEqualTo(TOP_BAR_HIDDEN);
61
         assertThat(result.topBarOptions.hidden).isEqualTo(TOP_BAR_HIDDEN);
62
         assertThat(result.topBarOptions.drawBehind).isEqualTo(TOP_BAR_DRAW_BEHIND);
62
         assertThat(result.topBarOptions.drawBehind).isEqualTo(TOP_BAR_DRAW_BEHIND);
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);
65
         assertThat(result.bottomTabsOptions.hidden).isEqualTo(BOTTOM_TABS_HIDDEN);
66
         assertThat(result.bottomTabsOptions.tabBadge).isEqualTo(BOTTOM_TABS_BADGE);
66
         assertThat(result.bottomTabsOptions.tabBadge).isEqualTo(BOTTOM_TABS_BADGE);
67
-        assertThat(result.bottomTabsOptions.currentTabId).isEqualTo(BOTTOM_TABS_CURRENT_TAB_ID);
67
+        assertThat(result.bottomTabsOptions.currentTabId.get()).isEqualTo(BOTTOM_TABS_CURRENT_TAB_ID);
68
         assertThat(result.bottomTabsOptions.currentTabIndex).isEqualTo(BOTTOM_TABS_CURRENT_TAB_INDEX);
68
         assertThat(result.bottomTabsOptions.currentTabIndex).isEqualTo(BOTTOM_TABS_CURRENT_TAB_INDEX);
69
     }
69
     }
70
 
70
 
81
     @NonNull
81
     @NonNull
82
     private JSONObject createTopBar() throws JSONException {
82
     private JSONObject createTopBar() throws JSONException {
83
         return new JSONObject()
83
         return new JSONObject()
84
-                .put("title", TITLE)
84
+                .put("title", "the title")
85
                 .put("backgroundColor", TOP_BAR_BACKGROUND_COLOR)
85
                 .put("backgroundColor", TOP_BAR_BACKGROUND_COLOR)
86
                 .put("textColor", TOP_BAR_TEXT_COLOR)
86
                 .put("textColor", TOP_BAR_TEXT_COLOR)
87
                 .put("textFontSize", TOP_BAR_FONT_SIZE)
87
                 .put("textFontSize", TOP_BAR_FONT_SIZE)
94
     @NonNull
94
     @NonNull
95
     private JSONObject createOtherTopBar() throws JSONException {
95
     private JSONObject createOtherTopBar() throws JSONException {
96
         return new JSONObject()
96
         return new JSONObject()
97
-                .put("title", TITLE)
97
+                .put("title", "the title")
98
                 .put("backgroundColor", TOP_BAR_BACKGROUND_COLOR)
98
                 .put("backgroundColor", TOP_BAR_BACKGROUND_COLOR)
99
                 .put("textColor", TOP_BAR_TEXT_COLOR)
99
                 .put("textColor", TOP_BAR_TEXT_COLOR)
100
                 .put("textFontSize", TOP_BAR_FONT_SIZE)
100
                 .put("textFontSize", TOP_BAR_FONT_SIZE)
142
     @Test
142
     @Test
143
     public void defaultEmptyOptions() throws Exception {
143
     public void defaultEmptyOptions() throws Exception {
144
         Options uut = new Options();
144
         Options uut = new Options();
145
-        assertThat(uut.topBarOptions.title).isEmpty();
145
+        assertThat(uut.topBarOptions.title.get("")).isEmpty();
146
     }
146
     }
147
 }
147
 }

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

76
         assertThat(uut.findControllerById("123")).isNull();
76
         assertThat(uut.findControllerById("123")).isNull();
77
         assertThat(uut.findControllerById(uut.getId())).isEqualTo(uut);
77
         assertThat(uut.findControllerById(uut.getId())).isEqualTo(uut);
78
         StackController inner = new StackController(activity, "inner");
78
         StackController inner = new StackController(activity, "inner");
79
-        inner.push(child1, new MockPromise());
79
+        inner.animatePush(child1, new MockPromise());
80
         assertThat(uut.findControllerById(child1.getId())).isNull();
80
         assertThat(uut.findControllerById(child1.getId())).isNull();
81
         uut.setTabs(Collections.singletonList(inner));
81
         uut.setTabs(Collections.singletonList(inner));
82
         assertThat(uut.findControllerById(child1.getId())).isEqualTo(child1);
82
         assertThat(uut.findControllerById(child1.getId())).isEqualTo(child1);

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

1
 package com.reactnativenavigation.viewcontrollers;
1
 package com.reactnativenavigation.viewcontrollers;
2
 
2
 
3
-import android.app.*;
3
+import android.app.Activity;
4
 
4
 
5
-import com.reactnativenavigation.*;
6
-import com.reactnativenavigation.mocks.*;
7
-import com.reactnativenavigation.parse.*;
5
+import com.reactnativenavigation.BaseTest;
6
+import com.reactnativenavigation.mocks.TestComponentLayout;
7
+import com.reactnativenavigation.parse.Options;
8
+import com.reactnativenavigation.views.StackLayout;
8
 
9
 
9
-import org.junit.*;
10
+import org.junit.Test;
10
 
11
 
11
-import static org.assertj.core.api.Java6Assertions.*;
12
-import static org.mockito.Mockito.*;
12
+import static org.assertj.core.api.Java6Assertions.assertThat;
13
+import static org.mockito.Mockito.spy;
14
+import static org.mockito.Mockito.times;
15
+import static org.mockito.Mockito.verify;
16
+import static org.mockito.Mockito.when;
13
 
17
 
14
 public class ComponentViewControllerTest extends BaseTest {
18
 public class ComponentViewControllerTest extends BaseTest {
15
     private ComponentViewController uut;
19
     private ComponentViewController uut;
20
+    private ParentController<StackLayout> parentController;
16
     private ComponentViewController.IReactView view;
21
     private ComponentViewController.IReactView view;
17
 
22
 
18
     @Override
23
     @Override
20
         super.beforeEach();
25
         super.beforeEach();
21
         Activity activity = newActivity();
26
         Activity activity = newActivity();
22
         view = spy(new TestComponentLayout(activity));
27
         view = spy(new TestComponentLayout(activity));
23
-        uut = new ComponentViewController(activity, "componentId1", "componentName", new ComponentViewController.ReactViewCreator() {
24
-            @Override
25
-            public ComponentViewController.IReactView create(final Activity activity1, final String componentId, final String componentName) {
26
-                return view;
27
-            }
28
-        }, new Options());
28
+        parentController = new StackController(activity, "stack");
29
+        uut = new ComponentViewController(activity, "componentId1", "componentName", (activity1, componentId, componentName) -> view, new Options());
30
+        uut.setParentController(parentController);
31
+        parentController.ensureViewIsCreated();
29
     }
32
     }
30
 
33
 
31
     @Test
34
     @Test

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

20
 public class NavigatorTest extends BaseTest {
20
 public class NavigatorTest extends BaseTest {
21
     private Activity activity;
21
     private Activity activity;
22
     private Navigator uut;
22
     private Navigator uut;
23
+    private ParentController parentController;
23
     private SimpleViewController child1;
24
     private SimpleViewController child1;
24
     private ViewController child2;
25
     private ViewController child2;
25
     private ViewController child3;
26
     private ViewController child3;
32
         super.beforeEach();
33
         super.beforeEach();
33
         activity = newActivity();
34
         activity = newActivity();
34
         uut = new Navigator(activity);
35
         uut = new Navigator(activity);
36
+        parentController = new StackController(activity, "stack");
37
+        parentController.ensureViewIsCreated();
35
         child1 = new SimpleViewController(activity, "child1");
38
         child1 = new SimpleViewController(activity, "child1");
36
         child2 = new SimpleViewController(activity, "child2");
39
         child2 = new SimpleViewController(activity, "child2");
37
         child3 = new SimpleViewController(activity, "child3");
40
         child3 = new SimpleViewController(activity, "child3");
42
     @Test
45
     @Test
43
     public void setRoot_AddsChildControllerView() throws Exception {
46
     public void setRoot_AddsChildControllerView() throws Exception {
44
         assertThat(uut.getView().getChildCount()).isZero();
47
         assertThat(uut.getView().getChildCount()).isZero();
45
-        uut.setRoot(child1);
48
+        uut.setRoot(child1, new MockPromise());
46
         assertIsChildById(uut.getView(), child1.getView());
49
         assertIsChildById(uut.getView(), child1.getView());
47
     }
50
     }
48
 
51
 
49
     @Test
52
     @Test
50
     public void setRoot_ReplacesExistingChildControllerViews() throws Exception {
53
     public void setRoot_ReplacesExistingChildControllerViews() throws Exception {
51
-        uut.setRoot(child1);
52
-        uut.setRoot(child2);
54
+        uut.setRoot(child1, new MockPromise());
55
+        uut.setRoot(child2, new MockPromise());
53
         assertIsChildById(uut.getView(), child2.getView());
56
         assertIsChildById(uut.getView(), child2.getView());
54
     }
57
     }
55
 
58
 
62
     @Test
65
     @Test
63
     public void push() throws Exception {
66
     public void push() throws Exception {
64
         StackController stackController = newStack();
67
         StackController stackController = newStack();
65
-        stackController.push(child1, new MockPromise());
66
-        uut.setRoot(stackController);
68
+        stackController.animatePush(child1, new MockPromise());
69
+        uut.setRoot(stackController, new MockPromise());
67
 
70
 
68
         assertIsChildById(uut.getView(), stackController.getView());
71
         assertIsChildById(uut.getView(), stackController.getView());
69
         assertIsChildById(stackController.getView(), child1.getView());
72
         assertIsChildById(stackController.getView(), child1.getView());
70
 
73
 
71
-        uut.push(child1.getId(), child2);
74
+        uut.push(child1.getId(), child2, new MockPromise());
72
 
75
 
73
         assertIsChildById(uut.getView(), stackController.getView());
76
         assertIsChildById(uut.getView(), stackController.getView());
74
         assertIsChildById(stackController.getView(), child2.getView());
77
         assertIsChildById(stackController.getView(), child2.getView());
76
 
79
 
77
     @Test
80
     @Test
78
     public void push_InvalidPushWithoutAStack_DoesNothing() throws Exception {
81
     public void push_InvalidPushWithoutAStack_DoesNothing() throws Exception {
79
-        uut.setRoot(child1);
80
-        uut.push(child1.getId(), child2);
82
+        uut.setRoot(child1, new MockPromise());
83
+        uut.push(child1.getId(), child2, new MockPromise());
81
         assertIsChildById(uut.getView(), child1.getView());
84
         assertIsChildById(uut.getView(), child1.getView());
82
     }
85
     }
83
 
86
 
86
         BottomTabsController bottomTabsController = newTabs();
89
         BottomTabsController bottomTabsController = newTabs();
87
         StackController stack1 = newStack();
90
         StackController stack1 = newStack();
88
         StackController stack2 = newStack();
91
         StackController stack2 = newStack();
89
-        stack1.push(child1, new MockPromise());
90
-        stack2.push(child2, new MockPromise());
92
+        stack1.animatePush(child1, new MockPromise());
93
+        stack2.animatePush(child2, new MockPromise());
91
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
94
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
92
-        uut.setRoot(bottomTabsController);
95
+        uut.setRoot(bottomTabsController, new MockPromise());
93
 
96
 
94
         SimpleViewController newChild = new SimpleViewController(activity, "new child");
97
         SimpleViewController newChild = new SimpleViewController(activity, "new child");
95
-        uut.push(child2.getId(), newChild);
98
+        uut.push(child2.getId(), newChild, new MockPromise());
96
 
99
 
97
         assertThat(stack1.getChildControllers()).doesNotContain(newChild);
100
         assertThat(stack1.getChildControllers()).doesNotContain(newChild);
98
         assertThat(stack2.getChildControllers()).contains(newChild);
101
         assertThat(stack2.getChildControllers()).contains(newChild);
100
 
103
 
101
     @Test
104
     @Test
102
     public void pop_InvalidDoesNothing() throws Exception {
105
     public void pop_InvalidDoesNothing() throws Exception {
103
-        uut.pop("123");
104
-        uut.setRoot(child1);
105
-        uut.pop(child1.getId());
106
+        uut.pop("123", new MockPromise());
107
+        uut.setRoot(child1, new MockPromise());
108
+        uut.pop(child1.getId(), new MockPromise());
106
         assertThat(uut.getChildControllers()).hasSize(1);
109
         assertThat(uut.getChildControllers()).hasSize(1);
107
     }
110
     }
108
 
111
 
111
         BottomTabsController bottomTabsController = newTabs();
114
         BottomTabsController bottomTabsController = newTabs();
112
         StackController stack1 = newStack();
115
         StackController stack1 = newStack();
113
         StackController stack2 = newStack();
116
         StackController stack2 = newStack();
114
-        stack1.push(child1, new MockPromise());
115
-        stack2.push(child2, new MockPromise());
116
-        stack2.push(child3, new MockPromise());
117
-        stack2.push(child4, new MockPromise());
118
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
117
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
119
-        uut.setRoot(bottomTabsController);
120
-
121
-        uut.pop("child4");
122
-
123
-        assertThat(stack2.getChildControllers()).containsOnly(child2, child3);
118
+        uut.setRoot(bottomTabsController, new MockPromise());
119
+        stack1.animatePush(child1, new MockPromise());
120
+        stack2.animatePush(child2, new MockPromise());
121
+        stack2.animatePush(child3, new MockPromise() {
122
+            @Override
123
+            public void resolve(@Nullable Object value) {
124
+                stack2.animatePush(child4, new MockPromise() {
125
+                    @Override
126
+                    public void resolve(@Nullable Object value) {
127
+                        uut.pop("child4", new MockPromise());
128
+                        assertThat(stack2.getChildControllers()).containsOnly(child2, child3);
129
+                    }
130
+                });
131
+            }
132
+        });
124
     }
133
     }
125
 
134
 
126
     @Test
135
     @Test
128
         BottomTabsController bottomTabsController = newTabs();
137
         BottomTabsController bottomTabsController = newTabs();
129
         StackController stack1 = newStack();
138
         StackController stack1 = newStack();
130
         StackController stack2 = newStack();
139
         StackController stack2 = newStack();
131
-        stack1.push(child1, new MockPromise());
132
-        stack2.push(child2, new MockPromise());
133
-        stack2.push(child3, new MockPromise());
134
-        stack2.push(child4, new MockPromise());
140
+        stack1.animatePush(child1, new MockPromise());
141
+        stack2.animatePush(child2, new MockPromise());
142
+        stack2.animatePush(child3, new MockPromise());
143
+        stack2.animatePush(child4, new MockPromise());
135
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
144
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
136
-        uut.setRoot(bottomTabsController);
145
+        uut.setRoot(bottomTabsController, new MockPromise());
137
 
146
 
138
-        uut.popSpecific(child2.getId());
147
+        uut.popSpecific(child2.getId(), new MockPromise());
139
 
148
 
140
         assertThat(stack2.getChildControllers()).containsOnly(child4, child3);
149
         assertThat(stack2.getChildControllers()).containsOnly(child4, child3);
141
     }
150
     }
145
         BottomTabsController bottomTabsController = newTabs();
154
         BottomTabsController bottomTabsController = newTabs();
146
         StackController stack1 = newStack();
155
         StackController stack1 = newStack();
147
         StackController stack2 = newStack();
156
         StackController stack2 = newStack();
148
-        stack1.push(child1, new MockPromise());
149
-        stack2.push(child2, new MockPromise());
150
-        stack2.push(child3, new MockPromise());
151
-        stack2.push(child4, new MockPromise());
152
-        stack2.push(child5, new MockPromise());
153
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
157
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
154
-        uut.setRoot(bottomTabsController);
158
+        uut.setRoot(bottomTabsController, new MockPromise());
155
 
159
 
156
-        uut.popTo(child2.getId());
157
-
158
-        assertThat(stack2.getChildControllers()).containsOnly(child2);
160
+        stack1.animatePush(child1, new MockPromise());
161
+        stack2.animatePush(child2, new MockPromise());
162
+        stack2.animatePush(child3, new MockPromise());
163
+        stack2.animatePush(child4, new MockPromise());
164
+        stack2.animatePush(child5, new MockPromise() {
165
+            @Override
166
+            public void resolve(@Nullable Object value) {
167
+                uut.popTo(child2.getId(), new MockPromise());
168
+                assertThat(stack2.getChildControllers()).containsOnly(child2);
169
+            }
170
+        });
159
     }
171
     }
160
 
172
 
161
     @Test
173
     @Test
163
         BottomTabsController bottomTabsController = newTabs();
175
         BottomTabsController bottomTabsController = newTabs();
164
         StackController stack1 = newStack();
176
         StackController stack1 = newStack();
165
         StackController stack2 = newStack();
177
         StackController stack2 = newStack();
166
-        stack1.push(child1, new MockPromise());
167
-        stack2.push(child2, new MockPromise());
168
-        stack2.push(child3, new MockPromise());
169
-        stack2.push(child4, new MockPromise());
170
-        stack2.push(child5, new MockPromise());
171
-
172
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
178
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
173
-        uut.setRoot(bottomTabsController);
179
+        uut.setRoot(bottomTabsController, new MockPromise());
174
 
180
 
175
-        uut.popToRoot(child3.getId());
181
+        stack1.animatePush(child1, new MockPromise());
182
+        stack2.animatePush(child2, new MockPromise());
183
+        stack2.animatePush(child3, new MockPromise());
184
+        stack2.animatePush(child4, new MockPromise());
185
+        stack2.animatePush(child5, new MockPromise() {
186
+            @Override
187
+            public void resolve(@Nullable Object value) {
188
+                uut.popToRoot(child3.getId(), new MockPromise());
176
 
189
 
177
-        assertThat(stack2.getChildControllers()).containsOnly(child2);
190
+                assertThat(stack2.getChildControllers()).containsOnly(child2);
191
+            }
192
+        });
178
     }
193
     }
179
 
194
 
180
     @Test
195
     @Test
181
     public void handleBack_DelegatesToRoot() throws Exception {
196
     public void handleBack_DelegatesToRoot() throws Exception {
182
         assertThat(uut.handleBack()).isFalse();
197
         assertThat(uut.handleBack()).isFalse();
183
         ViewController spy = spy(child1);
198
         ViewController spy = spy(child1);
184
-        uut.setRoot(spy);
199
+        uut.setRoot(spy, new MockPromise());
185
         when(spy.handleBack()).thenReturn(true);
200
         when(spy.handleBack()).thenReturn(true);
186
         assertThat(uut.handleBack()).isTrue();
201
         assertThat(uut.handleBack()).isTrue();
187
         verify(spy, times(1)).handleBack();
202
         verify(spy, times(1)).handleBack();
190
     @Test
205
     @Test
191
     public void setOptions_CallsApplyNavigationOptions() {
206
     public void setOptions_CallsApplyNavigationOptions() {
192
         ComponentViewController componentVc = new SimpleComponentViewController(activity, "theId");
207
         ComponentViewController componentVc = new SimpleComponentViewController(activity, "theId");
193
-        uut.setRoot(componentVc);
194
-        assertThat(componentVc.getOptions().topBarOptions.title).isEmpty();
208
+        componentVc.setParentController(parentController);
209
+        assertThat(componentVc.getOptions().topBarOptions.title.get("")).isEmpty();
210
+        uut.setRoot(componentVc, new MockPromise());
195
 
211
 
196
         Options options = new Options();
212
         Options options = new Options();
197
-        options.topBarOptions.title = "new title";
213
+        options.topBarOptions.title = new Text("new title");
198
 
214
 
199
         uut.setOptions("theId", options);
215
         uut.setOptions("theId", options);
200
-        assertThat(componentVc.getOptions().topBarOptions.title).isEqualTo("new title");
216
+        assertThat(componentVc.getOptions().topBarOptions.title.get()).isEqualTo("new title");
201
     }
217
     }
202
 
218
 
203
     @Test
219
     @Test
212
 
228
 
213
     @NonNull
229
     @NonNull
214
     private StackController newStack() {
230
     private StackController newStack() {
215
-        return new StackController(activity, "stack" + CompatUtils.generateViewId(), new TestNavigationAnimator());
231
+        return new StackController(activity, "stack" + CompatUtils.generateViewId());
216
     }
232
     }
217
 
233
 
218
     @Test
234
     @Test
219
     public void push_Promise() throws Exception {
235
     public void push_Promise() throws Exception {
220
         final StackController stackController = newStack();
236
         final StackController stackController = newStack();
221
-        stackController.push(child1, new MockPromise());
222
-        uut.setRoot(stackController);
237
+        stackController.animatePush(child1, new MockPromise());
238
+        uut.setRoot(stackController, new MockPromise());
223
 
239
 
224
         assertIsChildById(uut.getView(), stackController.getView());
240
         assertIsChildById(uut.getView(), stackController.getView());
225
         assertIsChildById(stackController.getView(), child1.getView());
241
         assertIsChildById(stackController.getView(), child1.getView());
235
 
251
 
236
     @Test
252
     @Test
237
     public void push_InvalidPushWithoutAStack_DoesNothing_Promise() throws Exception {
253
     public void push_InvalidPushWithoutAStack_DoesNothing_Promise() throws Exception {
238
-        uut.setRoot(child1);
254
+        uut.setRoot(child1, new MockPromise());
239
         uut.push(child1.getId(), child2, new MockPromise() {
255
         uut.push(child1.getId(), child2, new MockPromise() {
240
             @Override
256
             @Override
241
             public void reject(String code, Throwable e) {
257
             public void reject(String code, Throwable e) {
247
 
263
 
248
     @Test
264
     @Test
249
     public void pop_InvalidDoesNothing_Promise() throws Exception {
265
     public void pop_InvalidDoesNothing_Promise() throws Exception {
250
-        uut.pop("123");
251
-        uut.setRoot(child1);
266
+        uut.pop("123", new MockPromise());
267
+        uut.setRoot(child1, new MockPromise());
252
         uut.pop(child1.getId(), new MockPromise() {
268
         uut.pop(child1.getId(), new MockPromise() {
253
             @Override
269
             @Override
254
             public void reject(Throwable reason) {
270
             public void reject(Throwable reason) {
262
         BottomTabsController bottomTabsController = newTabs();
278
         BottomTabsController bottomTabsController = newTabs();
263
         StackController stack1 = newStack();
279
         StackController stack1 = newStack();
264
         final StackController stack2 = newStack();
280
         final StackController stack2 = newStack();
265
-        stack1.push(child1, new MockPromise());
266
-        stack2.push(child2, new MockPromise());
267
-        stack2.push(child3, new MockPromise());
268
-        stack2.push(child4, new MockPromise());
269
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
281
         bottomTabsController.setTabs(Arrays.asList(stack1, stack2));
270
-        uut.setRoot(bottomTabsController);
282
+        uut.setRoot(bottomTabsController, new MockPromise());
271
 
283
 
272
-        uut.pop("child4", new MockPromise() {
284
+        stack1.animatePush(child1, new MockPromise());
285
+        stack2.animatePush(child2, new MockPromise());
286
+        stack2.animatePush(child3, new MockPromise());
287
+        stack2.animatePush(child4, new MockPromise() {
273
             @Override
288
             @Override
274
             public void resolve(@Nullable Object value) {
289
             public void resolve(@Nullable Object value) {
290
+                uut.pop("child4", new MockPromise());
275
                 assertThat(stack2.getChildControllers()).containsOnly(child2, child3);
291
                 assertThat(stack2.getChildControllers()).containsOnly(child2, child3);
276
             }
292
             }
277
         });
293
         });
280
     @Test
296
     @Test
281
     public void pushIntoModal() throws Exception {
297
     public void pushIntoModal() throws Exception {
282
         StackController stackController = newStack();
298
         StackController stackController = newStack();
283
-        stackController.push(child1, new MockPromise());
299
+        stackController.animatePush(child1, new MockPromise());
284
         uut.showModal(stackController, new MockPromise());
300
         uut.showModal(stackController, new MockPromise());
285
-        uut.push(stackController.getId(), child2);
301
+        uut.push(stackController.getId(), child2, new MockPromise());
286
         assertIsChildById(stackController.getView(), child2.getView());
302
         assertIsChildById(stackController.getView(), child2.getView());
287
     }
303
     }
288
 }
304
 }

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

1
 package com.reactnativenavigation.viewcontrollers;
1
 package com.reactnativenavigation.viewcontrollers;
2
 
2
 
3
-import android.app.*;
3
+import android.app.Activity;
4
 import android.graphics.Color;
4
 import android.graphics.Color;
5
-import android.graphics.drawable.*;
6
-import android.view.*;
7
-import android.widget.*;
5
+import android.graphics.drawable.ColorDrawable;
6
+import android.view.View;
7
+import android.view.ViewGroup;
8
+import android.widget.RelativeLayout;
8
 
9
 
9
-import com.reactnativenavigation.*;
10
-import com.reactnativenavigation.mocks.*;
11
-import com.reactnativenavigation.parse.*;
10
+import com.reactnativenavigation.BaseTest;
11
+import com.reactnativenavigation.mocks.MockPromise;
12
+import com.reactnativenavigation.mocks.TestComponentLayout;
13
+import com.reactnativenavigation.parse.Fraction;
14
+import com.reactnativenavigation.parse.Options;
15
+import com.reactnativenavigation.parse.Text;
12
 
16
 
13
-import org.junit.*;
17
+import org.junit.Test;
14
 
18
 
15
-import static android.widget.RelativeLayout.*;
16
-import static org.assertj.core.api.Java6Assertions.*;
17
-import static org.mockito.Mockito.*;
19
+import javax.annotation.Nullable;
20
+
21
+import static android.widget.RelativeLayout.BELOW;
22
+import static org.assertj.core.api.Java6Assertions.assertThat;
23
+import static org.mockito.Mockito.spy;
18
 
24
 
19
 public class OptionsApplyingTest extends BaseTest {
25
 public class OptionsApplyingTest extends BaseTest {
20
     private Activity activity;
26
     private Activity activity;
27
+    private StackController stackController;
21
     private ComponentViewController uut;
28
     private ComponentViewController uut;
22
     private ComponentViewController.IReactView view;
29
     private ComponentViewController.IReactView view;
23
     private Options initialNavigationOptions;
30
     private Options initialNavigationOptions;
34
                 (activity1, componentId, componentName) -> view,
41
                 (activity1, componentId, componentName) -> view,
35
                 initialNavigationOptions
42
                 initialNavigationOptions
36
         );
43
         );
37
-        uut.ensureViewIsCreated();
44
+        stackController = new StackController(activity, "stack");
45
+        stackController.ensureViewIsCreated();
46
+        uut.setParentController(stackController);
38
     }
47
     }
39
 
48
 
40
     @Test
49
     @Test
41
     public void applyNavigationOptionsHandlesNoParentStack() throws Exception {
50
     public void applyNavigationOptionsHandlesNoParentStack() throws Exception {
51
+        uut.setParentController(null);
42
         assertThat(uut.getParentStackController()).isNull();
52
         assertThat(uut.getParentStackController()).isNull();
43
         uut.onViewAppeared();
53
         uut.onViewAppeared();
44
         assertThat(uut.getParentStackController()).isNull();
54
         assertThat(uut.getParentStackController()).isNull();
47
     @Test
57
     @Test
48
     public void initialOptionsAppliedOnAppear() throws Exception {
58
     public void initialOptionsAppliedOnAppear() throws Exception {
49
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
59
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
50
-        initialNavigationOptions.topBarOptions.title = "the title";
60
+        initialNavigationOptions.topBarOptions.title = new Text("the title");
51
         StackController stackController = new StackController(activity, "stackId");
61
         StackController stackController = new StackController(activity, "stackId");
52
-        stackController.push(uut, new MockPromise());
53
-        assertThat(uut.getTopBar().getTitle()).isEmpty();
62
+        stackController.animatePush(uut, new MockPromise() {});
63
+        assertThat(stackController.getTopBar().getTitle()).isEmpty();
54
 
64
 
55
         uut.onViewAppeared();
65
         uut.onViewAppeared();
56
-        assertThat(uut.getTopBar().getTitle()).isEqualTo("the title");
66
+        assertThat(stackController.getTopBar().getTitle()).isEqualTo("the title");
57
     }
67
     }
58
 
68
 
59
     @Test
69
     @Test
60
     public void mergeNavigationOptionsUpdatesCurrentOptions() throws Exception {
70
     public void mergeNavigationOptionsUpdatesCurrentOptions() throws Exception {
61
-        assertThat(uut.getOptions().topBarOptions.title).isEmpty();
71
+        uut.ensureViewIsCreated();
72
+        assertThat(uut.getOptions().topBarOptions.title.get("")).isEmpty();
62
         Options options = new Options();
73
         Options options = new Options();
63
-        options.topBarOptions.title = "new title";
74
+        options.topBarOptions.title = new Text("new title");
64
         uut.mergeOptions(options);
75
         uut.mergeOptions(options);
65
-        assertThat(uut.getOptions().topBarOptions.title).isEqualTo("new title");
76
+        assertThat(uut.getOptions().topBarOptions.title.get()).isEqualTo("new title");
66
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
77
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
67
     }
78
     }
68
 
79
 
69
     @Test
80
     @Test
70
     public void reappliesOptionsOnMerge() throws Exception {
81
     public void reappliesOptionsOnMerge() throws Exception {
71
         uut.onViewAppeared();
82
         uut.onViewAppeared();
72
-        assertThat(uut.getTopBar().getTitle()).isEmpty();
83
+        assertThat(stackController.getTopBar().getTitle()).isEmpty();
73
 
84
 
74
         Options opts = new Options();
85
         Options opts = new Options();
75
-        opts.topBarOptions.title = "the new title";
86
+        opts.topBarOptions.title = new Text("the new title");
76
         uut.mergeOptions(opts);
87
         uut.mergeOptions(opts);
77
 
88
 
78
-        assertThat(uut.getTopBar().getTitle()).isEqualTo("the new title");
89
+        assertThat(stackController.getTopBar().getTitle()).isEqualTo("the new title");
79
     }
90
     }
80
 
91
 
81
     @Test
92
     @Test
82
     public void appliesTopBackBackgroundColor() throws Exception {
93
     public void appliesTopBackBackgroundColor() throws Exception {
83
         uut.onViewAppeared();
94
         uut.onViewAppeared();
84
-        //TODO: FIX TEST
85
-        assertThat(((ColorDrawable) uut.getTopBar().getTitleBar().getBackground()).getColor()).isNotEqualTo(Color.RED);
86
 
95
 
87
         Options opts = new Options();
96
         Options opts = new Options();
88
-        opts.topBarOptions.backgroundColor = Color.RED;
97
+        opts.topBarOptions.backgroundColor = new com.reactnativenavigation.parse.Color(Color.RED);
89
         uut.mergeOptions(opts);
98
         uut.mergeOptions(opts);
90
 
99
 
91
-        assertThat(((ColorDrawable) uut.getTopBar().getTitleBar().getBackground()).getColor()).isEqualTo(Color.RED);
100
+        assertThat(((ColorDrawable) stackController.getTopBar().getTitleBar().getBackground()).getColor()).isEqualTo(Color.RED);
92
     }
101
     }
93
 
102
 
94
     @Test
103
     @Test
95
     public void appliesTopBarTextColor() throws Exception {
104
     public void appliesTopBarTextColor() throws Exception {
96
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
105
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
97
-        initialNavigationOptions.topBarOptions.title = "the title";
98
-        uut.onViewAppeared();
99
-        assertThat(uut.getTopBar().getTitleTextView().getCurrentTextColor()).isNotEqualTo(Color.RED);
100
-
101
-        Options opts = new Options();
102
-        opts.topBarOptions.title = "the title";
103
-        opts.topBarOptions.textColor = Color.RED;
104
-        uut.mergeOptions(opts);
105
-
106
-        assertThat(uut.getTopBar().getTitleTextView()).isNotEqualTo(null);
107
-        assertThat(uut.getTopBar().getTitleTextView().getCurrentTextColor()).isEqualTo(Color.RED);
106
+        initialNavigationOptions.topBarOptions.title = new Text("the title");
107
+        stackController.animatePush(uut, new MockPromise() {
108
+            @Override
109
+            public void resolve(@Nullable Object value) {
110
+                Options opts = new Options();
111
+                opts.topBarOptions.title = new Text("the title");
112
+                opts.topBarOptions.textColor = new com.reactnativenavigation.parse.Color(Color.RED);
113
+                uut.mergeOptions(opts);
114
+
115
+                assertThat(stackController.getTopBar().getTitleTextView()).isNotEqualTo(null);
116
+                assertThat(stackController.getTopBar().getTitleTextView().getCurrentTextColor()).isEqualTo(Color.RED);
117
+            }
118
+        });
108
     }
119
     }
109
 
120
 
110
     @Test
121
     @Test
111
     public void appliesTopBarTextSize() throws Exception {
122
     public void appliesTopBarTextSize() throws Exception {
112
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
123
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
113
-        initialNavigationOptions.topBarOptions.title = "the title";
124
+        initialNavigationOptions.topBarOptions.title = new Text("the title");
114
         uut.onViewAppeared();
125
         uut.onViewAppeared();
115
-        assertThat(uut.getTopBar().getTitleTextView().getTextSize()).isNotEqualTo(18);
116
 
126
 
117
         Options opts = new Options();
127
         Options opts = new Options();
118
-        opts.topBarOptions.title = "the title";
119
-        opts.topBarOptions.textFontSize = 18;
128
+        opts.topBarOptions.title = new Text("the title");
129
+        opts.topBarOptions.textFontSize = new Fraction(18);
120
         uut.mergeOptions(opts);
130
         uut.mergeOptions(opts);
121
 
131
 
122
-        assertThat(uut.getTopBar().getTitleTextView()).isNotEqualTo(null);
123
-        assertThat(uut.getTopBar().getTitleTextView().getTextSize()).isEqualTo(18);
132
+        assertThat(stackController.getTopBar().getTitleTextView()).isNotEqualTo(null);
133
+        assertThat(stackController.getTopBar().getTitleTextView().getTextSize()).isEqualTo(18);
124
     }
134
     }
125
 
135
 
126
     @Test
136
     @Test
127
     public void appliesTopBarHidden() throws Exception {
137
     public void appliesTopBarHidden() throws Exception {
128
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
138
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
129
-        initialNavigationOptions.topBarOptions.title = "the title";
139
+        initialNavigationOptions.topBarOptions.title = new Text("the title");
130
         uut.onViewAppeared();
140
         uut.onViewAppeared();
131
-        assertThat(uut.getTopBar().getVisibility()).isNotEqualTo(View.GONE);
141
+        assertThat(stackController.getTopBar().getVisibility()).isNotEqualTo(View.GONE);
132
 
142
 
133
         Options opts = new Options();
143
         Options opts = new Options();
134
         opts.topBarOptions.hidden = Options.BooleanOptions.True;
144
         opts.topBarOptions.hidden = Options.BooleanOptions.True;
135
         uut.mergeOptions(opts);
145
         uut.mergeOptions(opts);
136
 
146
 
137
-        assertThat(uut.getTopBar().getVisibility()).isEqualTo(View.GONE);
147
+        assertThat(stackController.getTopBar().getVisibility()).isEqualTo(View.GONE);
138
     }
148
     }
139
 
149
 
140
     @Test
150
     @Test
141
     public void appliesDrawUnder() throws Exception {
151
     public void appliesDrawUnder() throws Exception {
142
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
152
         assertThat(uut.getOptions()).isSameAs(initialNavigationOptions);
143
-        initialNavigationOptions.topBarOptions.title = "the title";
153
+        initialNavigationOptions.topBarOptions.title = new Text("the title");
144
         initialNavigationOptions.topBarOptions.drawBehind = Options.BooleanOptions.False;
154
         initialNavigationOptions.topBarOptions.drawBehind = Options.BooleanOptions.False;
145
         uut.onViewAppeared();
155
         uut.onViewAppeared();
146
-        RelativeLayout.LayoutParams uutLayoutParams = (RelativeLayout.LayoutParams) ((ViewGroup) uut.getComponent().asView()).getChildAt(1).getLayoutParams();
147
-        assertThat(uutLayoutParams.getRule(BELOW)).isNotEqualTo(0);
148
-
149
-        Options opts = new Options();
150
-        opts.topBarOptions.drawBehind = Options.BooleanOptions.True;
151
-        uut.mergeOptions(opts);
152
-
153
-        uutLayoutParams = (RelativeLayout.LayoutParams) ((ViewGroup) uut.getComponent().asView()).getChildAt(1).getLayoutParams();
154
-        assertThat(uutLayoutParams.getRule(BELOW)).isEqualTo(0);
156
+        stackController.animatePush(uut, new MockPromise() {
157
+            @Override
158
+            public void resolve(@Nullable Object value) {
159
+                RelativeLayout.LayoutParams uutLayoutParams = (RelativeLayout.LayoutParams) ((ViewGroup) uut.getComponent().asView()).getChildAt(0).getLayoutParams();
160
+                assertThat(uutLayoutParams.getRule(BELOW)).isNotEqualTo(0);
161
+
162
+                Options opts = new Options();
163
+                opts.topBarOptions.drawBehind = Options.BooleanOptions.True;
164
+                uut.mergeOptions(opts);
165
+
166
+                uutLayoutParams = (RelativeLayout.LayoutParams) (uut.getComponent().asView()).getLayoutParams();
167
+                assertThat(uutLayoutParams.getRule(BELOW)).isNotEqualTo(stackController.getTopBar().getId());
168
+            }
169
+        });
155
     }
170
     }
156
 }
171
 }

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

65
 
65
 
66
     @Test
66
     @Test
67
     public void findControllerById_Recursive() throws Exception {
67
     public void findControllerById_Recursive() throws Exception {
68
-        StackController stackController = new StackController(activity, "stack", new TestNavigationAnimator());
68
+        StackController stackController = new StackController(activity, "stack");
69
         SimpleViewController child1 = new SimpleViewController(activity, "child1");
69
         SimpleViewController child1 = new SimpleViewController(activity, "child1");
70
         SimpleViewController child2 = new SimpleViewController(activity, "child2");
70
         SimpleViewController child2 = new SimpleViewController(activity, "child2");
71
-        stackController.push(child1, new MockPromise());
72
-        stackController.push(child2, new MockPromise());
71
+        stackController.animatePush(child1, new MockPromise());
72
+        stackController.animatePush(child2, new MockPromise());
73
         children.add(stackController);
73
         children.add(stackController);
74
 
74
 
75
         assertThat(uut.findControllerById("child2")).isEqualTo(child2);
75
         assertThat(uut.findControllerById("child2")).isEqualTo(child2);

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

9
 import org.assertj.core.api.iterable.*;
9
 import org.assertj.core.api.iterable.*;
10
 import org.junit.*;
10
 import org.junit.*;
11
 
11
 
12
+import javax.annotation.Nullable;
13
+
12
 import static org.assertj.core.api.Java6Assertions.*;
14
 import static org.assertj.core.api.Java6Assertions.*;
13
 import static org.mockito.Mockito.*;
15
 import static org.mockito.Mockito.*;
14
 
16
 
24
     public void beforeEach() {
26
     public void beforeEach() {
25
         super.beforeEach();
27
         super.beforeEach();
26
         activity = newActivity();
28
         activity = newActivity();
27
-        uut = new StackController(activity, "uut", new TestNavigationAnimator());
29
+        uut = new StackController(activity, "uut");
28
         child1 = new SimpleViewController(activity, "child1");
30
         child1 = new SimpleViewController(activity, "child1");
29
         child2 = new SimpleViewController(activity, "child2");
31
         child2 = new SimpleViewController(activity, "child2");
30
         child3 = new SimpleViewController(activity, "child3");
32
         child3 = new SimpleViewController(activity, "child3");
38
     @Test
40
     @Test
39
     public void holdsAStackOfViewControllers() throws Exception {
41
     public void holdsAStackOfViewControllers() throws Exception {
40
         assertThat(uut.isEmpty()).isTrue();
42
         assertThat(uut.isEmpty()).isTrue();
41
-        uut.push(child1, new MockPromise());
42
-        uut.push(child2, new MockPromise());
43
-        uut.push(child3, new MockPromise());
43
+        uut.animatePush(child1, new MockPromise());
44
+        uut.animatePush(child2, new MockPromise());
45
+        uut.animatePush(child3, new MockPromise());
44
         assertThat(uut.peek()).isEqualTo(child3);
46
         assertThat(uut.peek()).isEqualTo(child3);
45
         assertContainsOnlyId(child1.getId(), child2.getId(), child3.getId());
47
         assertContainsOnlyId(child1.getId(), child2.getId(), child3.getId());
46
     }
48
     }
48
     @Test
50
     @Test
49
     public void push() throws Exception {
51
     public void push() throws Exception {
50
         assertThat(uut.isEmpty()).isTrue();
52
         assertThat(uut.isEmpty()).isTrue();
51
-        uut.push(child1, new MockPromise());
53
+        uut.animatePush(child1, new MockPromise());
52
         assertContainsOnlyId(child1.getId());
54
         assertContainsOnlyId(child1.getId());
53
     }
55
     }
54
 
56
 
55
     @Test
57
     @Test
56
     public void pop() throws Exception {
58
     public void pop() throws Exception {
57
-        uut.push(child1, new MockPromise());
58
-        uut.push(child2, new MockPromise());
59
-        assertContainsOnlyId(child2.getId(), child1.getId());
60
-        uut.pop(new MockPromise());
61
-        assertContainsOnlyId(child1.getId());
59
+        uut.animatePush(child1, new MockPromise());
60
+        uut.animatePush(child2, new MockPromise() {
61
+            @Override
62
+            public void resolve(@Nullable Object value) {
63
+                assertContainsOnlyId(child2.getId(), child1.getId());
64
+                uut.pop(new MockPromise());
65
+                assertContainsOnlyId(child1.getId());
66
+            }
67
+        });
62
     }
68
     }
63
 
69
 
64
     @Test
70
     @Test
66
         assertThat(uut.peek()).isNull();
72
         assertThat(uut.peek()).isNull();
67
         assertThat(uut.size()).isZero();
73
         assertThat(uut.size()).isZero();
68
         assertThat(uut.isEmpty()).isTrue();
74
         assertThat(uut.isEmpty()).isTrue();
69
-        uut.push(child1, new MockPromise());
75
+        uut.animatePush(child1, new MockPromise());
70
         assertThat(uut.peek()).isEqualTo(child1);
76
         assertThat(uut.peek()).isEqualTo(child1);
71
         assertThat(uut.size()).isEqualTo(1);
77
         assertThat(uut.size()).isEqualTo(1);
72
         assertThat(uut.isEmpty()).isFalse();
78
         assertThat(uut.isEmpty()).isFalse();
75
     @Test
81
     @Test
76
     public void pushAssignsRefToSelfOnPushedController() throws Exception {
82
     public void pushAssignsRefToSelfOnPushedController() throws Exception {
77
         assertThat(child1.getParentStackController()).isNull();
83
         assertThat(child1.getParentStackController()).isNull();
78
-        uut.push(child1, new MockPromise());
84
+        uut.animatePush(child1, new MockPromise());
79
         assertThat(child1.getParentStackController()).isEqualTo(uut);
85
         assertThat(child1.getParentStackController()).isEqualTo(uut);
80
 
86
 
81
-        StackController anotherNavController = new StackController(activity, "another", new TestNavigationAnimator());
82
-        anotherNavController.push(child2, new MockPromise());
87
+        StackController anotherNavController = new StackController(activity, "another");
88
+        anotherNavController.animatePush(child2, new MockPromise());
83
         assertThat(child2.getParentStackController()).isEqualTo(anotherNavController);
89
         assertThat(child2.getParentStackController()).isEqualTo(anotherNavController);
84
     }
90
     }
85
 
91
 
88
         assertThat(uut.isEmpty()).isTrue();
94
         assertThat(uut.isEmpty()).isTrue();
89
         assertThat(uut.handleBack()).isFalse();
95
         assertThat(uut.handleBack()).isFalse();
90
 
96
 
91
-        uut.push(child1, new MockPromise());
97
+        uut.animatePush(child1, new MockPromise());
92
         assertThat(uut.size()).isEqualTo(1);
98
         assertThat(uut.size()).isEqualTo(1);
93
         assertThat(uut.handleBack()).isFalse();
99
         assertThat(uut.handleBack()).isFalse();
94
 
100
 
95
-        uut.push(child2, new MockPromise());
96
-        assertThat(uut.size()).isEqualTo(2);
97
-        assertThat(uut.handleBack()).isTrue();
98
-        assertThat(uut.size()).isEqualTo(1);
99
-        assertThat(uut.handleBack()).isFalse();
101
+        uut.animatePush(child2, new MockPromise() {
102
+            @Override
103
+            public void resolve(@Nullable Object value) {
104
+                assertThat(uut.size()).isEqualTo(2);
105
+                assertThat(uut.handleBack()).isTrue();
106
+
107
+                assertThat(uut.size()).isEqualTo(1);
108
+                assertThat(uut.handleBack()).isFalse();
109
+            }
110
+        });
100
     }
111
     }
101
 
112
 
102
     @Test
113
     @Test
105
         uut.pop(new MockPromise());
116
         uut.pop(new MockPromise());
106
         assertThat(uut.isEmpty()).isTrue();
117
         assertThat(uut.isEmpty()).isTrue();
107
 
118
 
108
-        uut.push(child1, new MockPromise());
119
+        uut.animatePush(child1, new MockPromise());
109
         uut.pop(new MockPromise());
120
         uut.pop(new MockPromise());
110
         assertContainsOnlyId(child1.getId());
121
         assertContainsOnlyId(child1.getId());
111
     }
122
     }
114
     public void canPopWhenSizeIsMoreThanOne() throws Exception {
125
     public void canPopWhenSizeIsMoreThanOne() throws Exception {
115
         assertThat(uut.isEmpty()).isTrue();
126
         assertThat(uut.isEmpty()).isTrue();
116
         assertThat(uut.canPop()).isFalse();
127
         assertThat(uut.canPop()).isFalse();
117
-        uut.push(child1, new MockPromise());
128
+        uut.animatePush(child1, new MockPromise());
118
         assertContainsOnlyId(child1.getId());
129
         assertContainsOnlyId(child1.getId());
119
         assertThat(uut.canPop()).isFalse();
130
         assertThat(uut.canPop()).isFalse();
120
-        uut.push(child2, new MockPromise());
131
+        uut.animatePush(child2, new MockPromise());
121
         assertContainsOnlyId(child1.getId(), child2.getId());
132
         assertContainsOnlyId(child1.getId(), child2.getId());
122
         assertThat(uut.canPop()).isTrue();
133
         assertThat(uut.canPop()).isTrue();
123
     }
134
     }
125
     @Test
136
     @Test
126
     public void pushAddsToViewTree() throws Exception {
137
     public void pushAddsToViewTree() throws Exception {
127
         assertThat(uut.getView().findViewById(child1.getView().getId())).isNull();
138
         assertThat(uut.getView().findViewById(child1.getView().getId())).isNull();
128
-        uut.push(child1, new MockPromise());
139
+        uut.animatePush(child1, new MockPromise());
129
         assertThat(uut.getView().findViewById(child1.getView().getId())).isNotNull();
140
         assertThat(uut.getView().findViewById(child1.getView().getId())).isNotNull();
130
     }
141
     }
131
 
142
 
132
     @Test
143
     @Test
133
     public void pushRemovesPreviousFromTree() throws Exception {
144
     public void pushRemovesPreviousFromTree() throws Exception {
134
         assertThat(uut.getView().findViewById(child1.getView().getId())).isNull();
145
         assertThat(uut.getView().findViewById(child1.getView().getId())).isNull();
135
-        uut.push(child1, new MockPromise());
146
+        uut.animatePush(child1, new MockPromise());
136
         assertThat(uut.getView().findViewById(child1.getView().getId())).isNotNull();
147
         assertThat(uut.getView().findViewById(child1.getView().getId())).isNotNull();
137
-        uut.push(child2, new MockPromise());
138
-        assertThat(uut.getView().findViewById(child1.getView().getId())).isNull();
139
-        assertThat(uut.getView().findViewById(child2.getView().getId())).isNotNull();
148
+        uut.animatePush(child2, new MockPromise() {
149
+            @Override
150
+            public void resolve(@Nullable Object value) {
151
+                assertThat(uut.getView().findViewById(child1.getView().getId())).isNull();
152
+                assertThat(uut.getView().findViewById(child2.getView().getId())).isNotNull();
153
+            }
154
+        });
140
     }
155
     }
141
 
156
 
142
     @Test
157
     @Test
143
     public void popReplacesViewWithPrevious() throws Exception {
158
     public void popReplacesViewWithPrevious() throws Exception {
144
-        uut.push(child1, new MockPromise());
145
-        uut.push(child2, new MockPromise());
146
         final View child2View = child2.getView();
159
         final View child2View = child2.getView();
147
         final View child1View = child1.getView();
160
         final View child1View = child1.getView();
148
-        assertIsChildById(uut.getView(), child2View);
149
-        assertNotChildOf(uut.getView(), child1View);
150
-        uut.pop(new MockPromise());
151
-        assertNotChildOf(uut.getView(), child2View);
152
-        assertIsChildById(uut.getView(), child1View);
161
+
162
+        uut.animatePush(child1, new MockPromise());
163
+        uut.animatePush(child2, new MockPromise() {
164
+            @Override
165
+            public void resolve(@Nullable Object value) {
166
+                assertIsChildById(uut.getView(), child2View);
167
+                assertNotChildOf(uut.getView(), child1View);
168
+                uut.pop(new MockPromise());
169
+                assertNotChildOf(uut.getView(), child2View);
170
+                assertIsChildById(uut.getView(), child1View);
171
+            }
172
+        });
153
     }
173
     }
154
 
174
 
155
     @Test
175
     @Test
156
     public void popSpecificWhenTopIsRegularPop() throws Exception {
176
     public void popSpecificWhenTopIsRegularPop() throws Exception {
157
-        uut.push(child1, new MockPromise());
158
-        uut.push(child2, new MockPromise());
159
-        uut.popSpecific(child2);
160
-        assertContainsOnlyId(child1.getId());
161
-        assertIsChildById(uut.getView(), child1.getView());
177
+        uut.animatePush(child1, new MockPromise());
178
+        uut.animatePush(child2, new MockPromise() {
179
+            @Override
180
+            public void resolve(@Nullable Object value) {
181
+                uut.popSpecific(child2, new MockPromise() {
182
+                    @Override
183
+                    public void resolve(@Nullable Object value) {
184
+                        assertContainsOnlyId(child1.getId());
185
+                        assertIsChildById(uut.getView(), child1.getView());
186
+                    }
187
+                });
188
+            }
189
+        });
162
     }
190
     }
163
 
191
 
164
     @Test
192
     @Test
165
     public void popSpecificDeepInStack() throws Exception {
193
     public void popSpecificDeepInStack() throws Exception {
166
-        uut.push(child1, new MockPromise());
167
-        uut.push(child2, new MockPromise());
194
+        uut.animatePush(child1, new MockPromise());
195
+        uut.animatePush(child2, new MockPromise());
168
         assertIsChildById(uut.getView(), child2.getView());
196
         assertIsChildById(uut.getView(), child2.getView());
169
-        uut.popSpecific(child1);
197
+        uut.popSpecific(child1, new MockPromise());
170
         assertContainsOnlyId(child2.getId());
198
         assertContainsOnlyId(child2.getId());
171
         assertIsChildById(uut.getView(), child2.getView());
199
         assertIsChildById(uut.getView(), child2.getView());
172
     }
200
     }
173
 
201
 
174
     @Test
202
     @Test
175
     public void popTo_PopsTopUntilControllerIsNewTop() throws Exception {
203
     public void popTo_PopsTopUntilControllerIsNewTop() throws Exception {
176
-        uut.push(child1, new MockPromise());
177
-        uut.push(child2, new MockPromise());
178
-        uut.push(child3, new MockPromise());
179
-
180
-        assertThat(uut.size()).isEqualTo(3);
181
-        assertThat(uut.peek()).isEqualTo(child3);
182
-
183
-        uut.popTo(child1);
184
-
185
-        assertThat(uut.size()).isEqualTo(1);
186
-        assertThat(uut.peek()).isEqualTo(child1);
204
+        uut.animatePush(child1, new MockPromise());
205
+        uut.animatePush(child2, new MockPromise());
206
+        uut.animatePush(child3, new MockPromise() {
207
+            @Override
208
+            public void resolve(@Nullable Object value) {
209
+                assertThat(uut.size()).isEqualTo(3);
210
+                assertThat(uut.peek()).isEqualTo(child3);
211
+
212
+                uut.popTo(child1, new MockPromise());
213
+
214
+                assertThat(uut.size()).isEqualTo(1);
215
+                assertThat(uut.peek()).isEqualTo(child1);
216
+            }
217
+        });
187
     }
218
     }
188
 
219
 
189
     @Test
220
     @Test
190
     public void popTo_NotAChildOfThisStack_DoesNothing() throws Exception {
221
     public void popTo_NotAChildOfThisStack_DoesNothing() throws Exception {
191
-        uut.push(child1, new MockPromise());
192
-        uut.push(child3, new MockPromise());
222
+        uut.animatePush(child1, new MockPromise());
223
+        uut.animatePush(child3, new MockPromise());
193
         assertThat(uut.size()).isEqualTo(2);
224
         assertThat(uut.size()).isEqualTo(2);
194
-        uut.popTo(child2);
225
+        uut.popTo(child2, new MockPromise());
195
         assertThat(uut.size()).isEqualTo(2);
226
         assertThat(uut.size()).isEqualTo(2);
196
     }
227
     }
197
 
228
 
198
     @Test
229
     @Test
199
     public void popToRoot_PopsEverythingAboveFirstController() throws Exception {
230
     public void popToRoot_PopsEverythingAboveFirstController() throws Exception {
200
-        uut.push(child1, new MockPromise());
201
-        uut.push(child2, new MockPromise());
202
-        uut.push(child3, new MockPromise());
203
-
204
-        assertThat(uut.size()).isEqualTo(3);
205
-        assertThat(uut.peek()).isEqualTo(child3);
206
-
207
-        uut.popToRoot();
208
-
209
-        assertThat(uut.size()).isEqualTo(1);
210
-        assertThat(uut.peek()).isEqualTo(child1);
231
+        uut.animatePush(child1, new MockPromise());
232
+        uut.animatePush(child2, new MockPromise());
233
+        uut.animatePush(child3, new MockPromise() {
234
+            @Override
235
+            public void resolve(@Nullable Object value) {
236
+                assertThat(uut.size()).isEqualTo(3);
237
+                assertThat(uut.peek()).isEqualTo(child3);
238
+
239
+                uut.popToRoot(new MockPromise() {
240
+                    @Override
241
+                    public void resolve(@Nullable Object value) {
242
+                        assertThat(uut.size()).isEqualTo(1);
243
+                        assertThat(uut.peek()).isEqualTo(child1);
244
+                    }
245
+                });
246
+            }
247
+        });
211
     }
248
     }
212
 
249
 
213
     @Test
250
     @Test
214
     public void popToRoot_EmptyStackDoesNothing() throws Exception {
251
     public void popToRoot_EmptyStackDoesNothing() throws Exception {
215
         assertThat(uut.isEmpty()).isTrue();
252
         assertThat(uut.isEmpty()).isTrue();
216
-        uut.popToRoot();
253
+        uut.popToRoot(new MockPromise());
217
         assertThat(uut.isEmpty()).isTrue();
254
         assertThat(uut.isEmpty()).isTrue();
218
     }
255
     }
219
 
256
 
221
     public void findControllerById_ReturnsSelfOrChildrenById() throws Exception {
258
     public void findControllerById_ReturnsSelfOrChildrenById() throws Exception {
222
         assertThat(uut.findControllerById("123")).isNull();
259
         assertThat(uut.findControllerById("123")).isNull();
223
         assertThat(uut.findControllerById(uut.getId())).isEqualTo(uut);
260
         assertThat(uut.findControllerById(uut.getId())).isEqualTo(uut);
224
-        uut.push(child1, new MockPromise());
261
+        uut.animatePush(child1, new MockPromise());
225
         assertThat(uut.findControllerById(child1.getId())).isEqualTo(child1);
262
         assertThat(uut.findControllerById(child1.getId())).isEqualTo(child1);
226
     }
263
     }
227
 
264
 
228
     @Test
265
     @Test
229
     public void findControllerById_Deeply() throws Exception {
266
     public void findControllerById_Deeply() throws Exception {
230
-        StackController stack = new StackController(activity, "stack2", new TestNavigationAnimator());
231
-        stack.push(child2, new MockPromise());
232
-        uut.push(stack, new MockPromise());
267
+        StackController stack = new StackController(activity, "stack2");
268
+        stack.animatePush(child2, new MockPromise());
269
+        uut.animatePush(stack, new MockPromise());
233
         assertThat(uut.findControllerById(child2.getId())).isEqualTo(child2);
270
         assertThat(uut.findControllerById(child2.getId())).isEqualTo(child2);
234
     }
271
     }
235
 
272
 
238
         child1 = spy(child1);
275
         child1 = spy(child1);
239
         child2 = spy(child2);
276
         child2 = spy(child2);
240
         child3 = spy(child3);
277
         child3 = spy(child3);
241
-        uut.push(child1, new MockPromise());
242
-        uut.push(child2, new MockPromise());
243
-        uut.push(child3, new MockPromise());
244
-
245
-        verify(child3, times(0)).destroy();
246
-        uut.pop(new MockPromise());
247
-        verify(child3, times(1)).destroy();
278
+        uut.animatePush(child1, new MockPromise());
279
+        uut.animatePush(child2, new MockPromise());
280
+        uut.animatePush(child3, new MockPromise() {
281
+            @Override
282
+            public void resolve(@Nullable Object value) {
283
+                verify(child3, times(0)).destroy();
284
+                uut.pop(new MockPromise());
285
+                verify(child3, times(1)).destroy();
286
+            }
287
+        });
248
     }
288
     }
249
 
289
 
250
     @Test
290
     @Test
252
         child1 = spy(child1);
292
         child1 = spy(child1);
253
         child2 = spy(child2);
293
         child2 = spy(child2);
254
         child3 = spy(child3);
294
         child3 = spy(child3);
255
-        uut.push(child1, new MockPromise());
256
-        uut.push(child2, new MockPromise());
257
-        uut.push(child3, new MockPromise());
295
+        uut.animatePush(child1, new MockPromise());
296
+        uut.animatePush(child2, new MockPromise());
297
+        uut.animatePush(child3, new MockPromise());
258
 
298
 
259
         verify(child2, times(0)).destroy();
299
         verify(child2, times(0)).destroy();
260
-        uut.popSpecific(child2);
300
+        uut.popSpecific(child2, new MockPromise());
261
         verify(child2, times(1)).destroy();
301
         verify(child2, times(1)).destroy();
262
     }
302
     }
263
 
303
 
266
         child1 = spy(child1);
306
         child1 = spy(child1);
267
         child2 = spy(child2);
307
         child2 = spy(child2);
268
         child3 = spy(child3);
308
         child3 = spy(child3);
269
-        uut.push(child1, new MockPromise());
270
-        uut.push(child2, new MockPromise());
271
-        uut.push(child3, new MockPromise());
272
-
273
-        verify(child2, times(0)).destroy();
274
-        verify(child3, times(0)).destroy();
275
-        uut.popTo(child1);
276
-        verify(child2, times(1)).destroy();
277
-        verify(child3, times(1)).destroy();
309
+        uut.animatePush(child1, new MockPromise());
310
+        uut.animatePush(child2, new MockPromise());
311
+        uut.animatePush(child3, new MockPromise() {
312
+            @Override
313
+            public void resolve(@Nullable Object value) {
314
+                verify(child2, times(0)).destroy();
315
+                verify(child3, times(0)).destroy();
316
+
317
+                uut.popTo(child1, new MockPromise() {
318
+                    @Override
319
+                    public void resolve(@Nullable Object value) {
320
+                        verify(child2, times(1)).destroy();
321
+                        verify(child3, times(1)).destroy();
322
+                    }
323
+                });
324
+            }
325
+        });
278
     }
326
     }
279
 
327
 
280
     private void assertContainsOnlyId(String... ids) {
328
     private void assertContainsOnlyId(String... ids) {

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

23
 
23
 
24
     private TopTabsController uut;
24
     private TopTabsController uut;
25
     private List<TopTabLayoutMock> tabs = new ArrayList<>(SIZE);
25
     private List<TopTabLayoutMock> tabs = new ArrayList<>(SIZE);
26
-    private List<TopTabController> tabControllers = new ArrayList<>(SIZE);
26
+    private List<ViewController> tabControllers = new ArrayList<>(SIZE);
27
     private List<Options> tabOptions = new ArrayList<>(SIZE);
27
     private List<Options> tabOptions = new ArrayList<>(SIZE);
28
     private Options options;
28
     private Options options;
29
     private TopTabsLayout topTabsLayout;
29
     private TopTabsLayout topTabsLayout;
34
         tabControllers.clear();
34
         tabControllers.clear();
35
         tabs.clear();
35
         tabs.clear();
36
         Activity activity = newActivity();
36
         Activity activity = newActivity();
37
-        createTabs(activity);
37
+        tabControllers = createTabs(activity);
38
         options = new Options();
38
         options = new Options();
39
         topTabsLayout = spy(new TopTabsLayout(activity, tabControllers, new TopTabsAdapter(tabControllers)));
39
         topTabsLayout = spy(new TopTabsLayout(activity, tabControllers, new TopTabsAdapter(tabControllers)));
40
 
40
 
43
         uut = new TopTabsController(activity, "componentId", tabControllers, layoutCreator, options);
43
         uut = new TopTabsController(activity, "componentId", tabControllers, layoutCreator, options);
44
     }
44
     }
45
 
45
 
46
-    private void createTabs(Activity activity) {
46
+    private List<ViewController> createTabs(Activity activity) {
47
+        List<ViewController> result = new ArrayList<>(SIZE);
48
+        tabOptions = new ArrayList<>();
47
         for (int i = 0; i < SIZE; i++) {
49
         for (int i = 0; i < SIZE; i++) {
48
             TopTabLayoutMock reactView = spy(new TopTabLayoutMock(activity));
50
             TopTabLayoutMock reactView = spy(new TopTabLayoutMock(activity));
49
             tabs.add(reactView);
51
             tabs.add(reactView);
50
-            tabOptions.add(new Options());
51
-            tabControllers.add(spy(new TopTabController(activity,
52
+            Options options = new Options();
53
+            options.topTabOptions.title = new Text("Tab " + i);
54
+            tabOptions.add(options);
55
+            result.add(spy(new TopTabController(activity,
52
                     "idTab" + i,
56
                     "idTab" + i,
53
                     "tab" + i,
57
                     "tab" + i,
54
                     (activity1, componentId, componentName) -> reactView,
58
                     (activity1, componentId, componentName) -> reactView,
55
-                    tabOptions.get(i))
59
+                    options)
56
             ));
60
             ));
57
         }
61
         }
62
+        return result;
58
     }
63
     }
59
 
64
 
60
     @Test
65
     @Test

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

59
     public void holdsAReferenceToStackControllerOrNull() throws Exception {
59
     public void holdsAReferenceToStackControllerOrNull() throws Exception {
60
         assertThat(uut.getParentStackController()).isNull();
60
         assertThat(uut.getParentStackController()).isNull();
61
         StackController nav = new StackController(activity, "stack");
61
         StackController nav = new StackController(activity, "stack");
62
-        nav.push(uut, new MockPromise());
62
+        nav.animatePush(uut, new MockPromise());
63
         assertThat(uut.getParentStackController()).isEqualTo(nav);
63
         assertThat(uut.getParentStackController()).isEqualTo(nav);
64
     }
64
     }
65
 
65
 

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

9
 public class TopBarTest extends BaseTest {
9
 public class TopBarTest extends BaseTest {
10
     @Test
10
     @Test
11
     public void title() throws Exception {
11
     public void title() throws Exception {
12
-        TopBar topBar = new TopBar(newActivity(), null, null, buttonId -> {});
12
+        TopBar topBar = new TopBar(newActivity(), buttonId -> {});
13
         assertThat(topBar.getTitle()).isEmpty();
13
         assertThat(topBar.getTitle()).isEmpty();
14
 
14
 
15
         topBar.setTitle("new title");
15
         topBar.setTitle("new title");

+ 2
- 1
playground/src/screens/OptionsScreen.js View File

209
   root: {
209
   root: {
210
     flexGrow: 1,
210
     flexGrow: 1,
211
     justifyContent: 'center',
211
     justifyContent: 'center',
212
-    alignItems: 'center'
212
+    alignItems: 'center',
213
+    backgroundColor: 'white'
213
   },
214
   },
214
   h1: {
215
   h1: {
215
     fontSize: 24,
216
     fontSize: 24,

+ 6
- 1
playground/src/screens/PushedScreen.js View File

9
 const testIDs = require('../testIDs');
9
 const testIDs = require('../testIDs');
10
 
10
 
11
 class PushedScreen extends Component {
11
 class PushedScreen extends Component {
12
-  static get navigationOptions() {
12
+  static get options() {
13
     return {
13
     return {
14
       topBar: {
14
       topBar: {
15
         testID: testIDs.TOP_BAR_ELEMENT
15
         testID: testIDs.TOP_BAR_ELEMENT
49
         passProps: {
49
         passProps: {
50
           stackPosition: this.getStackPosition() + 1,
50
           stackPosition: this.getStackPosition() + 1,
51
           previousScreenIds: _.concat([], this.props.previousScreenIds || [], this.props.componentId)
51
           previousScreenIds: _.concat([], this.props.previousScreenIds || [], this.props.componentId)
52
+        },
53
+        options: {
54
+          topBar: {
55
+            title: `Pushed ${this.getStackPosition() + 1}`
56
+          }
52
         }
57
         }
53
       }
58
       }
54
     });
59
     });

+ 6
- 1
playground/src/screens/WelcomeScreen.js View File

168
   async onClickPush() {
168
   async onClickPush() {
169
     await Navigation.push(this.props.componentId, {
169
     await Navigation.push(this.props.componentId, {
170
       component: {
170
       component: {
171
-        name: 'navigation.playground.PushedScreen'
171
+        name: 'navigation.playground.PushedScreen',
172
+        options: {
173
+          topBar: {
174
+            title: 'pushed'
175
+          }
176
+        }
172
       }
177
       }
173
     });
178
     });
174
   }
179
   }