Browse Source

Merge remote-tracking branch 'origin/v2' into v2-rn51

Leo Natan 6 years ago
parent
commit
fd45a3ad60
No account linked to committer's email address
100 changed files with 2581 additions and 802 deletions
  1. 18
    3
      AndroidE2E/app/src/androidTest/java/com/reactnativenavigation/e2e/androide2e/OverlayTest.java
  2. 17
    0
      AndroidE2E/app/src/androidTest/java/com/reactnativenavigation/e2e/androide2e/StaticLifecycleEvents.java
  3. 2
    1
      Jenkinsfile
  4. 1
    1
      docs/_sidebar.md
  5. 4
    3
      docs/docs/CONTRIBUTING.md
  6. 3
    3
      docs/docs/Component.md
  7. 32
    33
      docs/docs/Navigation.md
  8. 2
    2
      docs/docs/Root.md
  9. 3
    2
      docs/docs/SideMenu.md
  10. 76
    12
      docs/docs/installation-android.md
  11. 1
    1
      docs/docs/options/BottomTabs.md
  12. 1
    1
      docs/docs/options/TopBar.md
  13. 76
    37
      docs/docs/usage.md
  14. 18
    18
      docs/docs/v1tov2diff.md
  15. 4
    3
      e2e/CustomTransition.js
  16. 12
    17
      e2e/Orientations.test.js
  17. 7
    1
      e2e/ScreenStack.test.js
  18. 21
    17
      e2e/ScreenStyle.test.js
  19. 3
    4
      e2e/TopLevelApi.test.js
  20. 4
    1
      lib/android/app/build.gradle
  21. 6
    0
      lib/android/app/src/main/java/com/reactnativenavigation/anim/AnimationListener.java
  22. 8
    0
      lib/android/app/src/main/java/com/reactnativenavigation/anim/FabAnimator.java
  23. 47
    0
      lib/android/app/src/main/java/com/reactnativenavigation/anim/FabCollapseBehaviour.java
  24. 42
    15
      lib/android/app/src/main/java/com/reactnativenavigation/anim/NavigationAnimator.java
  25. 16
    15
      lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarAnimator.java
  26. 49
    49
      lib/android/app/src/main/java/com/reactnativenavigation/anim/ViewAnimationSetBuilder.java
  27. 1
    0
      lib/android/app/src/main/java/com/reactnativenavigation/interfaces/ScrollEventListener.java
  28. 84
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/AnimationOptions.java
  29. 43
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/AnimationsOptions.java
  30. 55
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabOptions.java
  31. 55
    27
      lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabsOptions.java
  32. 130
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/FabOptions.java
  33. 9
    5
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java
  34. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutNode.java
  35. 80
    27
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java
  36. 6
    2
      lib/android/app/src/main/java/com/reactnativenavigation/parse/OverlayOptions.java
  37. 25
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/SideMenuOptions.java
  38. 27
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/SideMenuRootOptions.java
  39. 0
    7
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Text.java
  40. 37
    21
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java
  41. 9
    4
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabOptions.java
  42. 15
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabsOptions.java
  43. 69
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/ValueAnimationOptions.java
  44. 23
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Bool.java
  45. 14
    9
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java
  46. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Color.java
  47. 9
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/FloatParam.java
  48. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Fraction.java
  49. 26
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Interpolation.java
  50. 7
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/NullBool.java
  51. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/NullColor.java
  52. 14
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/NullFloatParam.java
  53. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/NullFraction.java
  54. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/NullNumber.java
  55. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/NullText.java
  56. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Number.java
  57. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Param.java
  58. 12
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Text.java
  59. 12
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/BoolParser.java
  60. 4
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/ColorParser.java
  61. 14
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/FloatParser.java
  62. 4
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/FractionParser.java
  63. 23
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/InterpolationParser.java
  64. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/JSONParser.java
  65. 3
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/LayoutNodeParser.java
  66. 4
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/NumberParser.java
  67. 4
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/TextParser.java
  68. 59
    0
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsOptionsPresenter.java
  69. 231
    0
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/FabOptionsPresenter.java
  70. 21
    12
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java
  71. 24
    0
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/SideMenuOptionsPresenter.java
  72. 17
    7
      lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationEvent.java
  73. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java
  74. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java
  75. 15
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ArrayUtils.java
  76. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/utils/CompatUtils.java
  77. 4
    4
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageLoader.java
  78. 26
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/UiUtils.java
  79. 28
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ViewUtils.java
  80. 0
    131
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/BottomTabsController.java
  81. 13
    9
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java
  82. 12
    56
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ModalStack.java
  83. 111
    105
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java
  84. 26
    3
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java
  85. 31
    7
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/SideMenuController.java
  86. 53
    32
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java
  87. 25
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java
  88. 36
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabFinder.java
  89. 152
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java
  90. 55
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/Modal.java
  91. 9
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalCreator.java
  92. 18
    4
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java
  93. 33
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/BottomTabs.java
  94. 13
    8
      lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentLayout.java
  95. 75
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/Fab.java
  96. 53
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/FabMenu.java
  97. 25
    15
      lib/android/app/src/main/java/com/reactnativenavigation/views/StackLayout.java
  98. 54
    22
      lib/android/app/src/main/java/com/reactnativenavigation/views/TitleBarButton.java
  99. 53
    20
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopBar.java
  100. 0
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabs.java

+ 18
- 3
AndroidE2E/app/src/androidTest/java/com/reactnativenavigation/e2e/androide2e/OverlayTest.java View File

1
 package com.reactnativenavigation.e2e.androide2e;
1
 package com.reactnativenavigation.e2e.androide2e;
2
 
2
 
3
 import android.support.test.uiautomator.By;
3
 import android.support.test.uiautomator.By;
4
+import android.support.test.uiautomator.UiObjectNotFoundException;
4
 
5
 
5
 import org.junit.Test;
6
 import org.junit.Test;
6
 
7
 
11
 		elementByText("PUSH OPTIONS SCREEN").click();
12
 		elementByText("PUSH OPTIONS SCREEN").click();
12
         elementByText("SHOW OVERLAY").click();
13
         elementByText("SHOW OVERLAY").click();
13
 		assertExists(By.text("Test view"));
14
 		assertExists(By.text("Test view"));
14
-		elementByText("OK").click();
15
-		assertExists(By.text("Overlay disappeared"));
16
-        elementByText("OK").click();
15
+        assetDismissed();
16
+	}
17
+
18
+    @Test
19
+	public void testOverlayNotInterceptingTouchEvents() throws Exception {
20
+		elementByText("PUSH OPTIONS SCREEN").click();
21
+        elementByText("SHOW TOUCH THROUGH OVERLAY").click();
22
+		assertExists(By.text("Test view"));
23
+        elementByText("DYNAMIC OPTIONS").click();
24
+        assertExists(By.text("Dynamic Title"));
25
+        assetDismissed();
17
 	}
26
 	}
27
+
28
+    private void assetDismissed() throws UiObjectNotFoundException {
29
+        elementByText("OK").click();
30
+        assertExists(By.text("Overlay disappeared"));
31
+        elementByText("OK").click();
32
+    }
18
 }
33
 }

+ 17
- 0
AndroidE2E/app/src/androidTest/java/com/reactnativenavigation/e2e/androide2e/StaticLifecycleEvents.java View File

1
+package com.reactnativenavigation.e2e.androide2e;
2
+
3
+import android.support.test.uiautomator.By;
4
+
5
+import org.junit.Test;
6
+
7
+public class StaticLifecycleEvents extends BaseTest {
8
+    @Test
9
+    public void didAppearDidDisappear() throws Exception {
10
+        elementByText("STATIC LIFECYCLE EVENTS").click();
11
+        assertExists(By.text("Static Lifecycle Events"));
12
+        assertExists(By.text("didAppear | navigation.playground.StaticLifecycleOverlay"));
13
+        elementByText("PUSH").click();
14
+        assertExists(By.text("didAppear | navigation.playground.PushedScreen"));
15
+        assertExists(By.text("didDisappear | navigation.playground.WelcomeScreen"));
16
+    }
17
+}

+ 2
- 1
Jenkinsfile View File

36
       }
36
       }
37
     }
37
     }
38
   }
38
   }
39
-}
39
+}
40
+

+ 1
- 1
docs/_sidebar.md View File

6
  - [Top Level](/docs/Navigation)
6
  - [Top Level](/docs/Navigation)
7
 - Params
7
 - Params
8
  - [Root](/docs/Root)
8
  - [Root](/docs/Root)
9
- - [Container](/docs/Container)
9
+ - [Component](/docs/Component)
10
  - [SideMenu](/docs/SideMenu)
10
  - [SideMenu](/docs/SideMenu)
11
 - Options
11
 - Options
12
  - [NavigationOptions](/docs/options/NavigationOptions)
12
  - [NavigationOptions](/docs/options/NavigationOptions)

+ 4
- 3
docs/docs/CONTRIBUTING.md View File

92
 | `lib` | The project itself composed of: |
92
 | `lib` | The project itself composed of: |
93
 | `lib/android` | android sources and unit tests |
93
 | `lib/android` | android sources and unit tests |
94
 | `lib/ios` | iOS sources and unit tests |
94
 | `lib/ios` | iOS sources and unit tests |
95
-| `lib/src` | javascript sources and unit tests |
96
-| `lib/src/index.js` | the entry point for `import Navigation from 'react-native-navigation'` |
95
+| `lib/src` | TypeScript sources and unit tests |
96
+| `lib/dist` | compiled javascript sources and unit tests |
97
+| `lib/dist/index.js` | the entry point for `import Navigation from 'react-native-navigation'` |
97
 | `e2e` | [detox](https://github.com/wix/detox) iOS e2e tests (in the future, once detox supports it, we will have android e2e here as well) |
98
 | `e2e` | [detox](https://github.com/wix/detox) iOS e2e tests (in the future, once detox supports it, we will have android e2e here as well) |
98
 | `AndroidE2E` | Android e2e tests using native uiautomator (until detox for android is ready) |
99
 | `AndroidE2E` | Android e2e tests using native uiautomator (until detox for android is ready) |
99
 | `playground` | The end-user project all e2e tests run against. Contains its own `src`, `android` and `ios`. Does not have its own package.json, depends on the local `<root>/lib` in order not to go through npm. |
100
 | `playground` | The end-user project all e2e tests run against. Contains its own `src`, `android` and `ios`. Does not have its own package.json, depends on the local `<root>/lib` in order not to go through npm. |
105
 | Command | Description |
106
 | Command | Description |
106
 | ------- | ----------- |
107
 | ------- | ----------- |
107
 | `npm install` | installs dependencies |
108
 | `npm install` | installs dependencies |
109
+| `npm run build` | compiles TypeScript sources `./lib/src` into javascript `./lib/dist` |
108
 | `npm run clean` | cleans all build directories, stops packager, fixes flakiness by removing watchman cache, etc. |
110
 | `npm run clean` | cleans all build directories, stops packager, fixes flakiness by removing watchman cache, etc. |
109
 | `npm run start` | starts the react-native packager for local debugging |
111
 | `npm run start` | starts the react-native packager for local debugging |
110
 | `npm run xcode` | for convenience, opens xcode in this project |
112
 | `npm run xcode` | for convenience, opens xcode in this project |
111
 | `npm run install-android`  |  builds playground debug/release version and installs on running android devices/emulators. <br> **Options:** `-- --release` |
113
 | `npm run install-android`  |  builds playground debug/release version and installs on running android devices/emulators. <br> **Options:** `-- --release` |
112
 | `npm run uninstall-android` | uninstalls playground from running android devices/simulators |
114
 | `npm run uninstall-android` | uninstalls playground from running android devices/simulators |
113
 | `npm run test-js` | runs javascript tests and coverage report |
115
 | `npm run test-js` | runs javascript tests and coverage report |
114
-| `npm run test-watch` | runs javascript tests in watch mode (can also use the provided wallaby config) |
115
 | `npm run test-unit-ios` | runs ios unit tests in debug/release <br> **Options:** `-- --release` |
116
 | `npm run test-unit-ios` | runs ios unit tests in debug/release <br> **Options:** `-- --release` |
116
 | `npm run test-unit-android` | runs android unit tests in debug/release <br> **Options:** `-- --release` |
117
 | `npm run test-unit-android` | runs android unit tests in debug/release <br> **Options:** `-- --release` |
117
 | `npm run test-e2e-ios` | runs the ios e2e suite (with detox) in debug/release <br> **Options:** `-- --release`|
118
 | `npm run test-e2e-ios` | runs the ios e2e suite (with detox) in debug/release <br> **Options:** `-- --release`|

docs/docs/Container.md → docs/docs/Component.md View File

1
-<h1>Container</h1>
1
+<h1>Component</h1>
2
 
2
 
3
 **Properties**
3
 **Properties**
4
 
4
 
5
 | Name | Type | Description |
5
 | Name | Type | Description |
6
 | --- | --- | --- |
6
 | --- | --- | --- |
7
-| name | <code>string</code> | The container's registered name |
8
-| topTabs | [<code>Array.&lt;Container&gt;</code>](#Container) |  |
7
+| name | <code>string</code> | The components registered name |
8
+| topTabs | [<code>Array.&lt;Component&gt;</code>](#Component) |  |
9
 | passProps | <code>object</code> | props |
9
 | passProps | <code>object</code> | props |
10
 | options | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Options">Options</a> |  |
10
 | options | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Options">Options</a> |  |
11
 
11
 

+ 32
- 33
docs/docs/Navigation.md View File

3
 # Navigation
3
 # Navigation
4
 
4
 
5
 * [Navigation](#Navigation)
5
 * [Navigation](#Navigation)
6
-    * [.registerContainer(containerName, getContainerFunc)](#Navigation+registerContainer)
6
+    * [.registerComponent(componentName, getComponentFunc)](#Navigation+registerComponent)
7
     * [.setRoot(root)](#Navigation+setRoot)
7
     * [.setRoot(root)](#Navigation+setRoot)
8
     * [.setDefaultOptions(options)](#Navigation+setDefaultOptions)
8
     * [.setDefaultOptions(options)](#Navigation+setDefaultOptions)
9
-    * [.setOptions(containerId, options)](#Navigation+setOptions)
9
+    * [.setOptions(componentId, options)](#Navigation+setOptions)
10
     * [.showModal(params)](#Navigation+showModal)
10
     * [.showModal(params)](#Navigation+showModal)
11
-    * [.dismissModal(containerId)](#Navigation+dismissModal)
11
+    * [.dismissModal(componentId)](#Navigation+dismissModal)
12
     * [.dismissAllModals()](#Navigation+dismissAllModals)
12
     * [.dismissAllModals()](#Navigation+dismissAllModals)
13
-    * [.push(containerId, container)](#Navigation+push)
14
-    * [.pop(containerId, params)](#Navigation+pop)
15
-    * [.popTo(containerId)](#Navigation+popTo)
16
-    * [.popToRoot(containerId)](#Navigation+popToRoot)
13
+    * [.push(componentId, component)](#Navigation+push)
14
+    * [.pop(componentId, params)](#Navigation+pop)
15
+    * [.popTo(componentId)](#Navigation+popTo)
16
+    * [.popToRoot(componentId)](#Navigation+popToRoot)
17
     * [.events()](#Navigation+events)
17
     * [.events()](#Navigation+events)
18
 
18
 
19
 
19
 
20
 * * *
20
 * * *
21
 
21
 
22
-<a name="Navigation+registerContainer"></a>
22
+<a name="Navigation+registerComponent"></a>
23
 
23
 
24
-## navigation.registerContainer(containerName, getContainerFunc)
24
+## navigation.registerComponent(componentName, getComponentFunc)
25
 Every screen component in your app must be registered with a unique name. The component itself is a traditional React component extending React.Component.
25
 Every screen component in your app must be registered with a unique name. The component itself is a traditional React component extending React.Component.
26
 
26
 
27
 
27
 
28
 | Param | Type | Description |
28
 | Param | Type | Description |
29
 | --- | --- | --- |
29
 | --- | --- | --- |
30
-| containerName | <code>string</code> | Unique container name |
31
-| getContainerFunc | <code>function</code> | generator function, typically `() => require('./myContainer')` |
30
+| componentName | <code>string</code> | Unique component name |
31
+| getComponentFunc | <code>function</code> | generator function, typically `() => require('./myComponent')` |
32
 
32
 
33
 
33
 
34
 * * *
34
 * * *
41
 
41
 
42
 | Param | Type |
42
 | Param | Type |
43
 | --- | --- |
43
 | --- | --- |
44
-| root | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Root">Root</a> | 
44
+| root | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Root">Root</a> |
45
 
45
 
46
 
46
 
47
 * * *
47
 * * *
54
 
54
 
55
 | Param | Type |
55
 | Param | Type |
56
 | --- | --- |
56
 | --- | --- |
57
-| options | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/options/NavigationOptions">NavigationOptions</a> | 
57
+| options | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/options/NavigationOptions">NavigationOptions</a> |
58
 
58
 
59
 
59
 
60
 * * *
60
 * * *
61
 
61
 
62
 <a name="Navigation+setOptions"></a>
62
 <a name="Navigation+setOptions"></a>
63
 
63
 
64
-## navigation.setOptions(containerId, options)
65
-Change a containers navigation options
64
+## navigation.setOptions(componentId, options)
65
+Change a components navigation options
66
 
66
 
67
 
67
 
68
 | Param | Type | Description |
68
 | Param | Type | Description |
69
 | --- | --- | --- |
69
 | --- | --- | --- |
70
-| containerId | <code>string</code> | The container's id. |
70
+| componentId | <code>string</code> | The component's id. |
71
 | options | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/options/NavigationOptions">NavigationOptions</a> |  |
71
 | options | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/options/NavigationOptions">NavigationOptions</a> |  |
72
 
72
 
73
 
73
 
81
 
81
 
82
 | Param | Type |
82
 | Param | Type |
83
 | --- | --- |
83
 | --- | --- |
84
-| params | <code>object</code> | 
84
+| params | <code>object</code> |
85
 
85
 
86
 
86
 
87
 * * *
87
 * * *
88
 
88
 
89
 <a name="Navigation+dismissModal"></a>
89
 <a name="Navigation+dismissModal"></a>
90
 
90
 
91
-## navigation.dismissModal(containerId)
92
-Dismiss a modal by containerId. The dismissed modal can be anywhere in the stack.
91
+## navigation.dismissModal(componentId)
92
+Dismiss a modal by componentId. The dismissed modal can be anywhere in the stack.
93
 
93
 
94
 
94
 
95
 | Param | Type | Description |
95
 | Param | Type | Description |
96
 | --- | --- | --- |
96
 | --- | --- | --- |
97
-| containerId | <code>string</code> | The container's id. |
97
+| componentId | <code>string</code> | The component's id. |
98
 
98
 
99
 
99
 
100
 * * *
100
 * * *
109
 
109
 
110
 <a name="Navigation+push"></a>
110
 <a name="Navigation+push"></a>
111
 
111
 
112
-## navigation.push(containerId, container)
112
+## navigation.push(componentId, component)
113
 Push a new screen into this screen's navigation stack.
113
 Push a new screen into this screen's navigation stack.
114
 
114
 
115
 
115
 
116
 | Param | Type | Description |
116
 | Param | Type | Description |
117
 | --- | --- | --- |
117
 | --- | --- | --- |
118
-| containerId | <code>string</code> | The container's id. |
119
-| container | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Container">Container</a> |  |
118
+| componentId | <code>string</code> | The component's id. |
119
+| component | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Component">Component</a> |  |
120
 
120
 
121
 
121
 
122
 * * *
122
 * * *
123
 
123
 
124
 <a name="Navigation+pop"></a>
124
 <a name="Navigation+pop"></a>
125
 
125
 
126
-## navigation.pop(containerId, params)
127
-Pop a container from the stack, regardless of it's position.
126
+## navigation.pop(componentId, params)
127
+Pop a component from the stack, regardless of it's position.
128
 
128
 
129
 
129
 
130
 | Param | Type | Description |
130
 | Param | Type | Description |
131
 | --- | --- | --- |
131
 | --- | --- | --- |
132
-| containerId | <code>string</code> | The container's id. |
132
+| componentId | <code>string</code> | The component's id. |
133
 | params | <code>*</code> |  |
133
 | params | <code>*</code> |  |
134
 
134
 
135
 
135
 
137
 
137
 
138
 <a name="Navigation+popTo"></a>
138
 <a name="Navigation+popTo"></a>
139
 
139
 
140
-## navigation.popTo(containerId)
141
-Pop the stack to a given container
140
+## navigation.popTo(componentId)
141
+Pop the stack to a given component
142
 
142
 
143
 
143
 
144
 | Param | Type | Description |
144
 | Param | Type | Description |
145
 | --- | --- | --- |
145
 | --- | --- | --- |
146
-| containerId | <code>string</code> | The container's id. |
146
+| componentId | <code>string</code> | The component's id. |
147
 
147
 
148
 
148
 
149
 * * *
149
 * * *
150
 
150
 
151
 <a name="Navigation+popToRoot"></a>
151
 <a name="Navigation+popToRoot"></a>
152
 
152
 
153
-## navigation.popToRoot(containerId)
154
-Pop the container's stack to root.
153
+## navigation.popToRoot(componentId)
154
+Pop the component's stack to root.
155
 
155
 
156
 
156
 
157
 | Param | Type |
157
 | Param | Type |
158
 | --- | --- |
158
 | --- | --- |
159
-| containerId | <code>*</code> | 
159
+| componentId | <code>*</code> |
160
 
160
 
161
 
161
 
162
 * * *
162
 * * *
165
 
165
 
166
 ## navigation.events()
166
 ## navigation.events()
167
 Obtain the events registery instance
167
 Obtain the events registery instance
168
-

+ 2
- 2
docs/docs/Root.md View File

4
 
4
 
5
 | Name | Type |
5
 | Name | Type |
6
 | --- | --- |
6
 | --- | --- |
7
-| container | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Container">Container</a> | 
7
+| component | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Component">Component</a> | 
8
 | sideMenu | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/SideMenu">SideMenu</a> | 
8
 | sideMenu | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/SideMenu">SideMenu</a> | 
9
-| bottomTabs | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Container">Container[]</a> | 
9
+| bottomTabs | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Component">Component[]</a> | 
10
 
10
 

+ 3
- 2
docs/docs/SideMenu.md View File

4
 
4
 
5
 | Name | Type |
5
 | Name | Type |
6
 | --- | --- |
6
 | --- | --- |
7
-| left | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Container">Container</a> | 
8
-| right | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Container">Container</a> | 
7
+| left | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Component">Component</a> | 
8
+| center | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Component">Component</a> | 
9
+| right | <a href="https://wix.github.io/react-native-navigation/v2/#/docs/Component">Component</a> | 
9
 
10
 

+ 76
- 12
docs/docs/installation-android.md View File

1
 # Android Installation
1
 # Android Installation
2
 
2
 
3
+!> Make sure your Android Studio installation is updated. We recommend editing `gralde` and `java` files in Android Studio as the ide will suggest fixes and point out errors, this way you avoid most common pitfalls.
4
+
5
+
3
 1. Install `react-native-navigation` latest stable version.
6
 1. Install `react-native-navigation` latest stable version.
4
 
7
 
5
 	```sh
8
 	```sh
12
 	include ':react-native-navigation'
15
 	include ':react-native-navigation'
13
 	project(':react-native-navigation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-navigation/lib/android/app/')
16
 	project(':react-native-navigation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-navigation/lib/android/app/')
14
 	```
17
 	```
18
+	
19
+3. Update `android/build.gradle`
20
+
21
+	```diff
22
+	buildscript {
23
+	    repositories {
24
+	+        mavenLocal()
25
+	+        mavenCentral()
26
+	+        google()
27
+	+        jcenter()
28
+	    }
29
+	    dependencies {
30
+	+        classpath 'com.android.tools.build:gradle:3.0.1'
31
+	-        classpath 'com.android.tools.build:gradle:2.2.3'
32
+	    }
33
+	}
34
+	
35
+	allprojects {
36
+	    repositories {
37
+	        mavenLocal()
38
+	+        mavenCentral()
39
+	+        google()
40
+	        jcenter()
41
+	        maven {
42
+	            // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
43
+	            url "$rootDir/../../node_modules/react-native/android"
44
+	        }
45
+	    }
46
+	}
47
+	```
48
+
49
+4. Update project dependencies in `android/app/build.gradle`.
15
 
50
 
16
-3. Update project dependencies in `android/app/build.gradle`.
17
 	```groovy
51
 	```groovy
18
 	android {
52
 	android {
19
-		compileSdkVersion 25
20
-		buildToolsVersion "25.0.1"
53
+	    compileSdkVersion 25
54
+	    buildToolsVersion "27.0.3"
55
+	    
56
+	    defaultConfig {
57
+	        minSdkVersion 19
58
+	        targetSdkVersion 25
21
 		...
59
 		...
60
+	    }
61
+	
62
+	    compileOptions {
63
+	        sourceCompatibility JavaVersion.VERSION_1_8
64
+	        targetCompatibility JavaVersion.VERSION_1_8
65
+	    }
66
+	    ...
22
 	}
67
 	}
23
-
68
+	
24
 	dependencies {
69
 	dependencies {
25
-		compile fileTree(dir: "libs", include: ["*.jar"])
26
-		compile "com.android.support:appcompat-v7:23.0.1"
27
-		compile "com.facebook.react:react-native:+"
28
-		compile project(':react-native-navigation')
70
+	    implementation fileTree(dir: "libs", include: ["*.jar"])
71
+	    implementation "com.android.support:appcompat-v7:25.4.0"
72
+	    implementation "com.facebook.react:react-native:+"
73
+	    implementation project(':react-native-navigation')
29
 	}
74
 	}
30
 	```
75
 	```
76
+	
77
+5. Make sure you're using the new gradle plugin, edit `android/gradle/wrapper/gradle-wrapper.properties`
78
+
79
+	```diff
80
+	distributionBase=GRADLE_USER_HOME
81
+	distributionPath=wrapper/dists
82
+	zipStoreBase=GRADLE_USER_HOME
83
+	zipStorePath=wrapper/dists
84
+	+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
85
+	-distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
86
+	```
87
+
88
+6. Update `gradle.properties` and disable incremental resource processing
89
+
90
+	```diff
91
+	+# Disable incremental resource processing as it broke relase build
92
+	+android.enableAapt2=false
93
+	```
31
 
94
 
32
-4. In `MainActivity.java` it should extend `com.reactnativenavigation.NavigationActivity` instead of `ReactActivity`.
95
+7. In `MainActivity.java` it should extend `com.reactnativenavigation.NavigationActivity` instead of `ReactActivity`.
33
 
96
 
34
 	This file can be located in `android/app/src/main/java/com/yourproject/`.
97
 	This file can be located in `android/app/src/main/java/com/yourproject/`.
35
 
98
 
43
 
106
 
44
 	If you have any **react-native** related methods, you can safely delete them.
107
 	If you have any **react-native** related methods, you can safely delete them.
45
 
108
 
46
-5. In `MainApplication.java`, add the following
109
+8. In `MainApplication.java`, add the following
110
+	
47
 	```java
111
 	```java
48
 	import com.reactnativenavigation.NavigationApplication;
112
 	import com.reactnativenavigation.NavigationApplication;
49
 
113
 
53
 		return BuildConfig.DEBUG;
117
 		return BuildConfig.DEBUG;
54
 	}
118
 	}
55
 
119
 
56
-	@Nullable
57
 	@Override
120
 	@Override
58
 	public List<ReactPackage> createAdditionalReactPackages() {
121
 	public List<ReactPackage> createAdditionalReactPackages() {
59
 		return Arrays.<ReactPackage>asList(
122
 		return Arrays.<ReactPackage>asList(
65
 
128
 
66
 	Make sure that `isDebug` methods is implemented.
129
 	Make sure that `isDebug` methods is implemented.
67
 
130
 
68
-6. Update `AndroidManifest.xml` and set **android:name** value to `.MainApplication`
131
+9. Update `AndroidManifest.xml` and set **android:name** value to `.MainApplication`
132
+	
69
 	```xml
133
 	```xml
70
 	<application
134
 	<application
71
 		android:name=".MainApplication"
135
 		android:name=".MainApplication"

+ 1
- 1
docs/docs/options/BottomTabs.md View File

7
 | currentTabId | <code>string</code> | 
7
 | currentTabId | <code>string</code> | 
8
 | currentTabIndex | <code>number</code> | 
8
 | currentTabIndex | <code>number</code> | 
9
 | hidden | <code>boolean</code> | 
9
 | hidden | <code>boolean</code> | 
10
-| animateHide | <code>boolean</code> | 
10
+| animate | <code>boolean</code> | 
11
 | testID | <code>string</code> | 
11
 | testID | <code>string</code> | 
12
 | drawUnder | <code>boolean</code> | 
12
 | drawUnder | <code>boolean</code> | 
13
 
13
 

+ 1
- 1
docs/docs/options/TopBar.md View File

12
 | textFontFamily | <code>string</code> | 
12
 | textFontFamily | <code>string</code> | 
13
 | testID | <code>string</code> | 
13
 | testID | <code>string</code> | 
14
 | hidden | <code>boolean</code> | 
14
 | hidden | <code>boolean</code> | 
15
-| animateHide | <code>boolean</code> | 
15
+| animate | <code>boolean</code> | 
16
 | hideOnScroll | <code>boolean</code> | 
16
 | hideOnScroll | <code>boolean</code> | 
17
 | transparent | <code>boolean</code> | 
17
 | transparent | <code>boolean</code> | 
18
 | translucent | <code>boolean</code> | 
18
 | translucent | <code>boolean</code> | 

+ 76
- 37
docs/docs/usage.md View File

4
 
4
 
5
 ### Navigation
5
 ### Navigation
6
 ```js
6
 ```js
7
-import Navigation from 'react-native-navigation';
7
+import { Navigation } from 'react-native-navigation';
8
 ```
8
 ```
9
 ### Events - On App Launched
9
 ### Events - On App Launched
10
 How to initiate your app.
10
 How to initiate your app.
12
 ```js
12
 ```js
13
 Navigation.events().onAppLaunched(() => {
13
 Navigation.events().onAppLaunched(() => {
14
   Navigation.setRoot({
14
   Navigation.setRoot({
15
-    container: {
15
+    component: {
16
       name: 'navigation.playground.WelcomeScreen'
16
       name: 'navigation.playground.WelcomeScreen'
17
     }
17
     }
18
   });
18
   });
19
 });
19
 });
20
 ```
20
 ```
21
 
21
 
22
-### registerContainer(screenID, generator)
22
+### registerComponent(screenID, generator)
23
 Every screen component in your app must be registered with a unique name. The component itself is a traditional React component extending React.Component.
23
 Every screen component in your app must be registered with a unique name. The component itself is a traditional React component extending React.Component.
24
 
24
 
25
 ```js
25
 ```js
26
-Navigation.registerContainer(`navigation.playground.WelcomeScreen`, () => WelcomeScreen);
26
+Navigation.registerComponent(`navigation.playground.WelcomeScreen`, () => WelcomeScreen);
27
 ```
27
 ```
28
 
28
 
29
 ### setRoot({params})
29
 ### setRoot({params})
31
 
31
 
32
 ```js
32
 ```js
33
 Navigation.setRoot({
33
 Navigation.setRoot({
34
-  container: {
35
-    name: 'navigation.playground.WelcomeScreen'
36
-  },
37
   sideMenu: {
34
   sideMenu: {
38
     left: {
35
     left: {
39
-      container: {
36
+      component: {
40
         name: 'navigation.playground.TextScreen',
37
         name: 'navigation.playground.TextScreen',
41
         passProps: {
38
         passProps: {
42
           text: 'This is a left side menu screen'
39
           text: 'This is a left side menu screen'
43
         }
40
         }
44
       }
41
       }
45
     },
42
     },
43
+    center: {
44
+      component: {
45
+        name: 'navigation.playground.WelcomeScreen'
46
+      },
47
+    },
46
     right: {
48
     right: {
47
-      container: {
49
+      component: {
48
         name: 'navigation.playground.TextScreen',
50
         name: 'navigation.playground.TextScreen',
49
         passProps: {
51
         passProps: {
50
           text: 'This is a right side menu screen'
52
           text: 'This is a right side menu screen'
58
 
60
 
59
 ```js
61
 ```js
60
 Navigation.setRoot({
62
 Navigation.setRoot({
61
-  bottomTabs: [
62
-    {
63
-      container: {
64
-        name: 'navigation.playground.TextScreen',
65
-        passProps: {
66
-          text: 'This is tab 1',
67
-          myFunction: () => 'Hello from a function!'
68
-        }
69
-      }
63
+  bottomTabs: {
64
+    children: [
65
+      {
66
+        component: {
67
+          name: 'navigation.playground.TextScreen',
68
+          passProps: {
69
+            text: 'This is tab 1',
70
+            myFunction: () => 'Hello from a function!',
71
+          },
72
+        },
73
+      },
74
+      {
75
+        component: {
76
+          name: 'navigation.playground.TextScreen',
77
+          passProps: {
78
+            text: 'This is tab 2',
79
+          },
80
+        },
81
+      },
82
+    ],
83
+  },
84
+});
85
+```
86
+
87
+Start a stack based app (with options):
88
+
89
+```js
90
+Navigation.setRoot({
91
+  stack: {
92
+    options: {
93
+      topBar: {
94
+        hidden: true,
95
+      },
70
     },
96
     },
71
-    {
72
-      container: {
73
-        name: 'navigation.playground.TextScreen',
74
-        passProps: {
75
-          text: 'This is tab 2'
76
-        }
77
-      }
78
-    }
79
-  ]
97
+    children: [
98
+      {
99
+        component: {
100
+          name: 'navigation.playground.TextScreen',
101
+          passProps: {
102
+            text: 'This is tab 1',
103
+            myFunction: () => 'Hello from a function!',
104
+          },
105
+        },
106
+      },
107
+      {
108
+        component: {
109
+          name: 'navigation.playground.TextScreen',
110
+          passProps: {
111
+            text: 'This is tab 2',
112
+          },
113
+        },
114
+      },
115
+    ],
116
+  },
80
 });
117
 });
81
 ```
118
 ```
82
 ## Screen API
119
 ## Screen API
85
 Push a new screen into this screen's navigation stack.
122
 Push a new screen into this screen's navigation stack.
86
 
123
 
87
 ```js
124
 ```js
88
-Navigation.push(this.props.containerId, {
89
-  name: 'navigation.playground.PushedScreen',
90
-  passProps: {}
125
+Navigation.push(this.props.componentId, {
126
+  component: {
127
+    name: 'navigation.playground.PushedScreen',
128
+    passProps: {}
129
+  }
91
 });
130
 });
92
 ```
131
 ```
93
-### pop(containerId)
132
+### pop(componentId)
94
 Pop the top screen from this screen's navigation stack.
133
 Pop the top screen from this screen's navigation stack.
95
 
134
 
96
 ```js
135
 ```js
97
-Navigation.pop(this.props.containerId);
136
+Navigation.pop(this.props.componentId);
98
 ```
137
 ```
99
-### popTo(containerId)
138
+### popTo(componentId)
100
 ```js
139
 ```js
101
 Navigation.popTo(previousScreenId);
140
 Navigation.popTo(previousScreenId);
102
 ```
141
 ```
104
 Pop all the screens until the root from this screen's navigation stack
143
 Pop all the screens until the root from this screen's navigation stack
105
 
144
 
106
 ```js
145
 ```js
107
-Navigation.popToRoot(this.props.containerId);
146
+Navigation.popToRoot(this.props.componentId);
108
 ```
147
 ```
109
 ### showModal(params = {})
148
 ### showModal(params = {})
110
 Show a screen as a modal.
149
 Show a screen as a modal.
111
 
150
 
112
 ```js
151
 ```js
113
 Navigation.showModal({
152
 Navigation.showModal({
114
-  container: {
153
+  component: {
115
     name: 'navigation.playground.ModalScreen',
154
     name: 'navigation.playground.ModalScreen',
116
     passProps: {
155
     passProps: {
117
         key: 'value'
156
         key: 'value'
119
   }
158
   }
120
 });
159
 });
121
 ```
160
 ```
122
-### dismissModal(containerId)
161
+### dismissModal(componentId)
123
 Dismiss modal.
162
 Dismiss modal.
124
 
163
 
125
 ```js
164
 ```js
126
-Navigation.dismissModal(this.props.containerId);
165
+Navigation.dismissModal(this.props.componentId);
127
 ```
166
 ```
128
 ### dismissAllModals()
167
 ### dismissAllModals()
129
 Dismiss all the current modals at the same time.
168
 Dismiss all the current modals at the same time.

+ 18
- 18
docs/docs/v1tov2diff.md View File

17
 There are ways to solve some of these problems in v1 but they are not straightforward. We want to change that.
17
 There are ways to solve some of these problems in v1 but they are not straightforward. We want to change that.
18
 
18
 
19
 #### New API
19
 #### New API
20
-To solve this problem in v2, every screen receives as a prop it’s containerId. Whenever you want to perform an action from that screen you need to pass the containerId to the function:
20
+To solve this problem in v2, every screen receives as a prop it’s componentId. Whenever you want to perform an action from that screen you need to pass the componentId to the function:
21
 ```js
21
 ```js
22
-Navigator.pop(this.props.containerId)
22
+Navigator.pop(this.props.componentId)
23
 ```
23
 ```
24
 ### Built for Contributors
24
 ### Built for Contributors
25
 Currently, it requires a lot of work to accept pull requests. We need to manually make sure that everything works before we approve them because v1 is not thoroughly tested. <br>
25
 Currently, it requires a lot of work to accept pull requests. We need to manually make sure that everything works before we approve them because v1 is not thoroughly tested. <br>
179
 ```js
179
 ```js
180
 Navigation.events().onAppLaunched(() => {
180
 Navigation.events().onAppLaunched(() => {
181
     Navigation.setRoot({
181
     Navigation.setRoot({
182
-      container: {
182
+      component: {
183
         name: 'navigation.playground.WelcomeScreen'
183
         name: 'navigation.playground.WelcomeScreen'
184
       }
184
       }
185
     });
185
     });
186
   });
186
   });
187
 ```
187
 ```
188
 
188
 
189
-#### registerContainer(screenID, generator)
189
+#### registerComponent(screenID, generator)
190
 Every screen component in your app must be registered with a unique name. The component itself is a traditional React component extending React.Component.
190
 Every screen component in your app must be registered with a unique name. The component itself is a traditional React component extending React.Component.
191
 
191
 
192
 ```js
192
 ```js
193
-Navigation.registerContainer(`navigation.playground.WelcomeScreen`, () => WelcomeScreen);
193
+Navigation.registerComponent(`navigation.playground.WelcomeScreen`, () => WelcomeScreen);
194
 ```
194
 ```
195
 
195
 
196
 #### setRoot({params})
196
 #### setRoot({params})
198
 
198
 
199
 ```js
199
 ```js
200
 Navigation.setRoot({
200
 Navigation.setRoot({
201
-      container: {
201
+      component: {
202
         name: 'navigation.playground.WelcomeScreen'
202
         name: 'navigation.playground.WelcomeScreen'
203
       },
203
       },
204
       sideMenu: {
204
       sideMenu: {
205
         left: {
205
         left: {
206
-          container: {
206
+          component: {
207
             name: 'navigation.playground.TextScreen',
207
             name: 'navigation.playground.TextScreen',
208
             passProps: {
208
             passProps: {
209
               text: 'This is a left side menu screen'
209
               text: 'This is a left side menu screen'
211
           }
211
           }
212
         },
212
         },
213
         right: {
213
         right: {
214
-          container: {
214
+          component: {
215
             name: 'navigation.playground.TextScreen',
215
             name: 'navigation.playground.TextScreen',
216
             passProps: {
216
             passProps: {
217
               text: 'This is a right side menu screen'
217
               text: 'This is a right side menu screen'
227
 Navigation.setRoot({
227
 Navigation.setRoot({
228
       tabs: [
228
       tabs: [
229
         {
229
         {
230
-          container: {
230
+          component: {
231
             name: 'navigation.playground.TextScreen',
231
             name: 'navigation.playground.TextScreen',
232
             passProps: {
232
             passProps: {
233
               text: 'This is tab 1',
233
               text: 'This is tab 1',
236
           }
236
           }
237
         },
237
         },
238
         {
238
         {
239
-          container: {
239
+          component: {
240
             name: 'navigation.playground.TextScreen',
240
             name: 'navigation.playground.TextScreen',
241
             passProps: {
241
             passProps: {
242
               text: 'This is tab 2'
242
               text: 'This is tab 2'
252
 Push a new screen into this screen's navigation stack.
252
 Push a new screen into this screen's navigation stack.
253
 
253
 
254
 ```js
254
 ```js
255
-Navigation.push(this.props.containerId, {
255
+Navigation.push(this.props.componentId, {
256
       name: 'navigation.playground.PushedScreen',
256
       name: 'navigation.playground.PushedScreen',
257
       passProps: {}
257
       passProps: {}
258
     });
258
     });
259
 ```
259
 ```
260
-#### pop(containerId)
260
+#### pop(componentId)
261
 Pop the top screen from this screen's navigation stack.
261
 Pop the top screen from this screen's navigation stack.
262
 
262
 
263
 ```js
263
 ```js
264
-Navigation.pop(this.props.containerId);
264
+Navigation.pop(this.props.componentId);
265
 ```
265
 ```
266
 #### popTo(params)
266
 #### popTo(params)
267
 
267
 
268
 ```js
268
 ```js
269
-Navigation.popTo(this.props.containerId, this.props.previousScreenIds[0]);
269
+Navigation.popTo(this.props.componentId, this.props.previousScreenIds[0]);
270
 ```
270
 ```
271
 #### popToRoot()
271
 #### popToRoot()
272
 Pop all the screens until the root from this screen's navigation stack
272
 Pop all the screens until the root from this screen's navigation stack
273
 
273
 
274
 ```js
274
 ```js
275
-Navigation.popToRoot(this.props.containerId);
275
+Navigation.popToRoot(this.props.componentId);
276
 ```
276
 ```
277
 #### showModal(params = {})
277
 #### showModal(params = {})
278
 Show a screen as a modal.
278
 Show a screen as a modal.
279
 
279
 
280
 ```js
280
 ```js
281
 Navigation.showModal({
281
 Navigation.showModal({
282
-      container: {
282
+      component: {
283
         name: 'navigation.playground.ModalScreen',
283
         name: 'navigation.playground.ModalScreen',
284
         passProps: {
284
         passProps: {
285
             key: 'value'
285
             key: 'value'
287
       }
287
       }
288
     });
288
     });
289
 ```
289
 ```
290
-#### dismissModal(containerId)
290
+#### dismissModal(componentId)
291
 Dismiss modal.
291
 Dismiss modal.
292
 
292
 
293
 ```js
293
 ```js
294
-Navigation.dismissModal(this.props.containerId);
294
+Navigation.dismissModal(this.props.componentId);
295
 ```
295
 ```
296
 #### dismissAllModals()
296
 #### dismissAllModals()
297
 Dismiss all the current modals at the same time.
297
 Dismiss all the current modals at the same time.

+ 4
- 3
e2e/CustomTransition.js View File

1
 
1
 
2
 const Utils = require('./Utils');
2
 const Utils = require('./Utils');
3
+const testIDs = require('../playground/src/testIDs');
3
 
4
 
4
-const elementByLabel = Utils.elementByLabel;
5
+const elementById = Utils.elementById;
5
 
6
 
6
 describe('custom transition', () => {
7
 describe('custom transition', () => {
7
   beforeEach(async () => {
8
   beforeEach(async () => {
9
   });
10
   });
10
 
11
 
11
   it('sanity', async () => {
12
   it('sanity', async () => {
12
-    await elementByLabel('Push Options Screen').tap();
13
-    await elementByLabel('Custom Transition').tap();
13
+    await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
14
+    await elementById(testIDs.CUSTOM_TRANSITION_BUTTON).tap();
14
     await expect(element(by.id('shared_image1'))).toExist();
15
     await expect(element(by.id('shared_image1'))).toExist();
15
     await element(by.id('shared_image1')).tap();
16
     await element(by.id('shared_image1')).tap();
16
     await expect(element(by.id('shared_image2'))).toExist();
17
     await expect(element(by.id('shared_image2'))).toExist();

+ 12
- 17
e2e/Orientations.test.js View File

4
 
4
 
5
 const { elementById } = Utils;
5
 const { elementById } = Utils;
6
 
6
 
7
-describe('orientation', () => {
7
+describe(':ios: orientation', () => {
8
   beforeEach(async () => {
8
   beforeEach(async () => {
9
     await device.relaunchApp();
9
     await device.relaunchApp();
10
   });
10
   });
11
 
11
 
12
-  afterEach(async () => {
13
-    await device.setOrientation('landscape');
14
-    await device.setOrientation('portrait');
15
-  });
16
-
17
   it('default allows all', async () => {
12
   it('default allows all', async () => {
18
     await elementById(testIDs.ORIENTATION_BUTTON).tap();
13
     await elementById(testIDs.ORIENTATION_BUTTON).tap();
19
     await elementById(testIDs.DEFAULT_ORIENTATION_BUTTON).tap();
14
     await elementById(testIDs.DEFAULT_ORIENTATION_BUTTON).tap();
20
-    await expect(element(by.id('currentOrientation'))).toHaveText('Portrait');
15
+    await expect(elementById(testIDs.PORTRAIT_ELEMENT)).toBeVisible();
21
     await device.setOrientation('landscape');
16
     await device.setOrientation('landscape');
22
-    await expect(element(by.id('currentOrientation'))).toHaveText('Landscape');
17
+    await expect(elementById(testIDs.LANDSCAPE_ELEMENT)).toBeVisible();
23
     await device.setOrientation('portrait');
18
     await device.setOrientation('portrait');
24
-    await expect(element(by.id('currentOrientation'))).toHaveText('Portrait');
19
+    await expect(elementById(testIDs.PORTRAIT_ELEMENT)).toBeVisible();
25
     await elementById(testIDs.DISMISS_BUTTON).tap();
20
     await elementById(testIDs.DISMISS_BUTTON).tap();
26
   });
21
   });
27
 
22
 
28
   it('landscape and portrait array', async () => {
23
   it('landscape and portrait array', async () => {
29
     await elementById(testIDs.ORIENTATION_BUTTON).tap();
24
     await elementById(testIDs.ORIENTATION_BUTTON).tap();
30
     await elementById(testIDs.LANDSCAPE_PORTRAIT_ORIENTATION_BUTTON).tap();
25
     await elementById(testIDs.LANDSCAPE_PORTRAIT_ORIENTATION_BUTTON).tap();
31
-    await expect(element(by.id('currentOrientation'))).toHaveText('Portrait');
26
+    await expect(element(by.id(testIDs.PORTRAIT_ELEMENT))).toBeVisible();
32
     await device.setOrientation('landscape');
27
     await device.setOrientation('landscape');
33
-    await expect(element(by.id('currentOrientation'))).toHaveText('Landscape');
28
+    await expect(element(by.id(testIDs.LANDSCAPE_ELEMENT))).toBeVisible();
34
     await device.setOrientation('portrait');
29
     await device.setOrientation('portrait');
35
-    await expect(element(by.id('currentOrientation'))).toHaveText('Portrait');
30
+    await expect(element(by.id(testIDs.PORTRAIT_ELEMENT))).toBeVisible();
36
     await elementById(testIDs.DISMISS_BUTTON).tap();
31
     await elementById(testIDs.DISMISS_BUTTON).tap();
37
   });
32
   });
38
 
33
 
39
   it('portrait only', async () => {
34
   it('portrait only', async () => {
40
     await elementById(testIDs.ORIENTATION_BUTTON).tap();
35
     await elementById(testIDs.ORIENTATION_BUTTON).tap();
41
     await elementById(testIDs.PORTRAIT_ORIENTATION_BUTTON).tap();
36
     await elementById(testIDs.PORTRAIT_ORIENTATION_BUTTON).tap();
42
-    await expect(element(by.id('currentOrientation'))).toHaveText('Portrait');
37
+    await expect(elementById(testIDs.PORTRAIT_ELEMENT)).toBeVisible();
43
     await device.setOrientation('landscape');
38
     await device.setOrientation('landscape');
44
-    await expect(element(by.id('currentOrientation'))).toHaveText('Portrait');
39
+    await expect(elementById(testIDs.PORTRAIT_ELEMENT)).toBeVisible();
45
     await device.setOrientation('portrait');
40
     await device.setOrientation('portrait');
46
-    await expect(element(by.id('currentOrientation'))).toHaveText('Portrait');
41
+    await expect(elementById(testIDs.PORTRAIT_ELEMENT)).toBeVisible();
47
     await elementById(testIDs.DISMISS_BUTTON).tap();
42
     await elementById(testIDs.DISMISS_BUTTON).tap();
48
   });
43
   });
49
 
44
 
51
     await elementById(testIDs.ORIENTATION_BUTTON).tap();
46
     await elementById(testIDs.ORIENTATION_BUTTON).tap();
52
     await elementById(testIDs.LANDSCAPE_ORIENTATION_BUTTON).tap();
47
     await elementById(testIDs.LANDSCAPE_ORIENTATION_BUTTON).tap();
53
     await device.setOrientation('landscape');
48
     await device.setOrientation('landscape');
54
-    await expect(element(by.id('currentOrientation'))).toHaveText('Landscape');
49
+    await expect(element(by.id(testIDs.LANDSCAPE_ELEMENT))).toBeVisible();
55
     await device.setOrientation('portrait');
50
     await device.setOrientation('portrait');
56
-    await expect(element(by.id('currentOrientation'))).toHaveText('Landscape');
51
+    await expect(element(by.id(testIDs.LANDSCAPE_ELEMENT))).toBeVisible();
57
     await elementById(testIDs.DISMISS_BUTTON).tap();
52
     await elementById(testIDs.DISMISS_BUTTON).tap();
58
   });
53
   });
59
 });
54
 });

+ 7
- 1
e2e/ScreenStack.test.js View File

90
     await elementById(testIDs.SHOW_MODAL_BUTTON).tap();
90
     await elementById(testIDs.SHOW_MODAL_BUTTON).tap();
91
     await elementById(testIDs.MODAL_WITH_STACK_BUTTON).tap();
91
     await elementById(testIDs.MODAL_WITH_STACK_BUTTON).tap();
92
     await expect(elementByLabel('Screen 2')).toBeVisible();
92
     await expect(elementByLabel('Screen 2')).toBeVisible();
93
-    await Utils.tapBackIos();
93
+    await elementById(testIDs.POP_BUTTON).tap();
94
     await expect(elementByLabel('Screen 1')).toBeVisible();
94
     await expect(elementByLabel('Screen 1')).toBeVisible();
95
   });
95
   });
96
+
97
+  it(':ios: push native component with options', async () => {
98
+    await elementById(testIDs.PUSH_NATIVE_COMPONENT_BUTTON).tap();
99
+    await expect(elementById('TestLabel')).toBeVisible();
100
+    await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
101
+  });
96
 });
102
 });

+ 21
- 17
e2e/ScreenStyle.test.js View File

10
 
10
 
11
   it('declare a options on component component', async () => {
11
   it('declare a options on component component', async () => {
12
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
12
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
13
-    await expect(element(by.label('Static Title'))).toBeVisible();
13
+    await expect(elementByLabel('Static Title')).toBeVisible();
14
   });
14
   });
15
 
15
 
16
   it('change title on component component', async () => {
16
   it('change title on component component', async () => {
17
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
17
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
18
-    await expect(element(by.label('Static Title'))).toBeVisible();
18
+    await expect(elementByLabel('Static Title')).toBeVisible();
19
     await elementById(testIDs.DYNAMIC_OPTIONS_BUTTON).tap();
19
     await elementById(testIDs.DYNAMIC_OPTIONS_BUTTON).tap();
20
-    await expect(element(by.label('Dynamic Title'))).toBeVisible();
20
+    await expect(elementByLabel('Dynamic Title')).toBeVisible();
21
   });
21
   });
22
 
22
 
23
   it('set dynamic options with valid options will do something and not crash', async () => {
23
   it('set dynamic options with valid options will do something and not crash', async () => {
35
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
35
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
36
   });
36
   });
37
 
37
 
38
-  it('hides topBar onScroll down and shows it on scroll up', async () => {
38
+  it(':ios: hides topBar onScroll down and shows it on scroll up', async () => {
39
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
39
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
40
     await elementById(testIDs.SCROLLVIEW_SCREEN_BUTTON).tap();
40
     await elementById(testIDs.SCROLLVIEW_SCREEN_BUTTON).tap();
41
     await elementById(testIDs.TOGGLE_TOP_BAR_HIDE_ON_SCROLL).tap();
41
     await elementById(testIDs.TOGGLE_TOP_BAR_HIDE_ON_SCROLL).tap();
42
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
42
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
43
-    await element(by.id(testIDs.SCROLLVIEW_ELEMENT)).swipe('up', 'fast');
43
+    await element(by.id(testIDs.SCROLLVIEW_ELEMENT)).swipe('up', 'slow');
44
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeNotVisible();
44
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeNotVisible();
45
-    await element(by.id(testIDs.SCROLLVIEW_ELEMENT)).swipe('down', 'fast');
45
+    await element(by.id(testIDs.SCROLLVIEW_ELEMENT)).swipe('down', 'slow');
46
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
46
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
47
   });
47
   });
48
 
48
 
49
-  it('makes topBar transparent and opaque', async () => {
50
-    await elementByLabel('Push Options Screen').tap();
51
-    await elementByLabel('Top Bar Transparent').tap();
52
-    await expect(element(by.type('_UIVisualEffectBackdropView'))).toBeNotVisible();
53
-    await elementByLabel('Top Bar Opaque').tap();
54
-    await expect(element(by.type('_UIVisualEffectBackdropView')).atIndex(1)).toBeVisible();
55
-  });
56
-
57
   it('set Tab Bar badge on a current Tab', async () => {
49
   it('set Tab Bar badge on a current Tab', async () => {
58
     await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
50
     await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
59
     await elementById(testIDs.SET_TAB_BADGE_BUTTON).tap();
51
     await elementById(testIDs.SET_TAB_BADGE_BUTTON).tap();
60
     await expect(element(by.text('TeSt'))).toBeVisible();
52
     await expect(element(by.text('TeSt'))).toBeVisible();
61
   });
53
   });
62
 
54
 
63
-  it('hide Tab Bar', async () => {
55
+  it(':ios: hide Tab Bar', async () => {
64
     await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
56
     await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
65
     await expect(elementById(testIDs.BOTTOM_TABS_ELEMENT)).toBeVisible();
57
     await expect(elementById(testIDs.BOTTOM_TABS_ELEMENT)).toBeVisible();
66
     await elementById(testIDs.HIDE_BOTTOM_TABS_BUTTON).tap();
58
     await elementById(testIDs.HIDE_BOTTOM_TABS_BUTTON).tap();
67
     await expect(elementById(testIDs.BOTTOM_TABS_ELEMENT)).toBeNotVisible();
59
     await expect(elementById(testIDs.BOTTOM_TABS_ELEMENT)).toBeNotVisible();
68
   });
60
   });
69
 
61
 
70
-  it('show Tab Bar', async () => {
62
+  it(':ios: show Tab Bar', async () => {
71
     await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
63
     await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
72
     await elementById(testIDs.HIDE_BOTTOM_TABS_BUTTON).tap();
64
     await elementById(testIDs.HIDE_BOTTOM_TABS_BUTTON).tap();
73
     await expect(elementById(testIDs.BOTTOM_TABS_ELEMENT)).toBeNotVisible();
65
     await expect(elementById(testIDs.BOTTOM_TABS_ELEMENT)).toBeNotVisible();
91
     await expect(elementById(testIDs.CENTERED_TEXT_HEADER)).toBeVisible();
83
     await expect(elementById(testIDs.CENTERED_TEXT_HEADER)).toBeVisible();
92
   });
84
   });
93
 
85
 
94
-  it('set right buttons', async () => {
86
+  it(':ios: set right buttons', async () => {
95
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
87
     await elementById(testIDs.PUSH_OPTIONS_BUTTON).tap();
96
     await expect(elementById('buttonOne')).toBeVisible();
88
     await expect(elementById('buttonOne')).toBeVisible();
97
     await elementById('buttonOne').tap();
89
     await elementById('buttonOne').tap();
126
     await elementById(testIDs.POP_BUTTON).tap();
118
     await elementById(testIDs.POP_BUTTON).tap();
127
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
119
     await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
128
   });
120
   });
121
+
122
+  it('supports user-provided id', async () => {
123
+    await elementById(testIDs.PROVIDED_ID).tap();
124
+    await expect(elementByLabel('User provided id')).toBeVisible();
125
+  });
126
+
127
+  it('stack options should not override component options', async () => {
128
+    await elementById(testIDs.TAB_BASED_APP_BUTTON).tap();
129
+    await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeNotVisible();
130
+    await elementById(testIDs.SECOND_TAB_BAR_BUTTON).tap();
131
+    await expect(elementById(testIDs.TOP_BAR_ELEMENT)).toBeVisible();
132
+  });
129
 });
133
 });

+ 3
- 4
e2e/TopLevelApi.test.js View File

18
     await expect(elementByLabel('Hello from a function!')).toBeVisible();
18
     await expect(elementByLabel('Hello from a function!')).toBeVisible();
19
   });
19
   });
20
 
20
 
21
-  it('switch to tabs with side menus', async () => {
21
+  it(':ios: switch to tabs with side menus', async () => {
22
     await elementById(testIDs.TAB_BASED_APP_SIDE_BUTTON).tap();
22
     await elementById(testIDs.TAB_BASED_APP_SIDE_BUTTON).tap();
23
     await elementById(testIDs.CENTERED_TEXT_HEADER).swipe('right');
23
     await elementById(testIDs.CENTERED_TEXT_HEADER).swipe('right');
24
     await expect(elementById(testIDs.HIDE_LEFT_SIDE_MENU_BUTTON)).toBeVisible();
24
     await expect(elementById(testIDs.HIDE_LEFT_SIDE_MENU_BUTTON)).toBeVisible();
28
     await elementById(testIDs.PUSH_LIFECYCLE_BUTTON).tap();
28
     await elementById(testIDs.PUSH_LIFECYCLE_BUTTON).tap();
29
     await expect(elementByLabel('didAppear')).toBeVisible();
29
     await expect(elementByLabel('didAppear')).toBeVisible();
30
     await elementById(testIDs.PUSH_TO_TEST_DID_DISAPPEAR_BUTTON).tap();
30
     await elementById(testIDs.PUSH_TO_TEST_DID_DISAPPEAR_BUTTON).tap();
31
-    await expect(elementByLabel('Alert')).toBeVisible();
32
     await expect(elementByLabel('didDisappear')).toBeVisible();
31
     await expect(elementByLabel('didDisappear')).toBeVisible();
33
   });
32
   });
34
 
33
 
35
   it('unmount is called on pop', async () => {
34
   it('unmount is called on pop', async () => {
36
     await elementById(testIDs.PUSH_LIFECYCLE_BUTTON).tap();
35
     await elementById(testIDs.PUSH_LIFECYCLE_BUTTON).tap();
37
     await expect(elementByLabel('didAppear')).toBeVisible();
36
     await expect(elementByLabel('didAppear')).toBeVisible();
38
-    await Utils.tapBackIos();
37
+    await elementById(testIDs.POP_BUTTON).tap();
39
     await expect(elementByLabel('componentWillUnmount')).toBeVisible();
38
     await expect(elementByLabel('componentWillUnmount')).toBeVisible();
40
-    await element(by.traits(['button']).and(by.label('OK'))).atIndex(0).tap();
39
+    await elementByLabel('OK').atIndex(0).tap();
41
     await expect(elementByLabel('didDisappear')).toBeVisible();
40
     await expect(elementByLabel('didDisappear')).toBeVisible();
42
   });
41
   });
43
 });
42
 });

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

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

+ 6
- 0
lib/android/app/src/main/java/com/reactnativenavigation/anim/AnimationListener.java View File

1
+package com.reactnativenavigation.anim;
2
+
3
+
4
+public interface AnimationListener {
5
+    void onAnimationEnd();
6
+}

+ 8
- 0
lib/android/app/src/main/java/com/reactnativenavigation/anim/FabAnimator.java View File

1
+package com.reactnativenavigation.anim;
2
+
3
+
4
+public interface FabAnimator {
5
+    void show();
6
+
7
+    void hide();
8
+}

+ 47
- 0
lib/android/app/src/main/java/com/reactnativenavigation/anim/FabCollapseBehaviour.java View File

1
+package com.reactnativenavigation.anim;
2
+
3
+
4
+import android.support.annotation.NonNull;
5
+
6
+import com.reactnativenavigation.interfaces.ScrollEventListener;
7
+
8
+public class FabCollapseBehaviour implements ScrollEventListener.OnScrollListener, ScrollEventListener.OnDragListener {
9
+
10
+    private FabAnimator fabAnimator;
11
+    private ScrollEventListener scrollEventListener;
12
+
13
+    public FabCollapseBehaviour(FabAnimator fabAnimator) {
14
+        this.fabAnimator = fabAnimator;
15
+    }
16
+
17
+    public void enableCollapse(@NonNull ScrollEventListener scrollEventListener) {
18
+        this.scrollEventListener = scrollEventListener;
19
+        this.scrollEventListener.register(null, this, this);
20
+    }
21
+
22
+    public void disableCollapse() {
23
+        if (scrollEventListener != null) {
24
+            scrollEventListener.unregister();
25
+        }
26
+    }
27
+
28
+    @Override
29
+    public void onScrollUp(float nextTranslation) {
30
+        //empty
31
+    }
32
+
33
+    @Override
34
+    public void onScrollDown(float nextTranslation) {
35
+        //empty
36
+    }
37
+
38
+    @Override
39
+    public void onShow() {
40
+        fabAnimator.show();
41
+    }
42
+
43
+    @Override
44
+    public void onHide() {
45
+        fabAnimator.hide();
46
+    }
47
+}

+ 42
- 15
lib/android/app/src/main/java/com/reactnativenavigation/anim/NavigationAnimator.java View File

5
 import android.animation.AnimatorSet;
5
 import android.animation.AnimatorSet;
6
 import android.animation.ObjectAnimator;
6
 import android.animation.ObjectAnimator;
7
 import android.content.Context;
7
 import android.content.Context;
8
+import android.support.annotation.NonNull;
8
 import android.support.annotation.Nullable;
9
 import android.support.annotation.Nullable;
9
 import android.view.View;
10
 import android.view.View;
10
 import android.view.animation.AccelerateInterpolator;
11
 import android.view.animation.AccelerateInterpolator;
11
 import android.view.animation.DecelerateInterpolator;
12
 import android.view.animation.DecelerateInterpolator;
12
 
13
 
14
+import com.reactnativenavigation.parse.AnimationsOptions;
13
 import com.reactnativenavigation.utils.UiUtils;
15
 import com.reactnativenavigation.utils.UiUtils;
14
 
16
 
15
 @SuppressWarnings("ResourceType")
17
 @SuppressWarnings("ResourceType")
16
 public class NavigationAnimator {
18
 public class NavigationAnimator {
17
 
19
 
18
-    public interface NavigationAnimationListener {
19
-        void onAnimationEnd();
20
-    }
20
+    private AnimationsOptions options = new AnimationsOptions();
21
 
21
 
22
     private static final int DURATION = 300;
22
     private static final int DURATION = 300;
23
     private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
23
     private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
28
         translationY = UiUtils.getWindowHeight(context);
28
         translationY = UiUtils.getWindowHeight(context);
29
     }
29
     }
30
 
30
 
31
-    public void animatePush(final View view, @Nullable final NavigationAnimationListener animationListener) {
31
+    public void animatePush(final View view, @Nullable final AnimationListener animationListener) {
32
         view.setVisibility(View.INVISIBLE);
32
         view.setVisibility(View.INVISIBLE);
33
-        ObjectAnimator alpha = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1);
34
-        alpha.setInterpolator(DECELERATE_INTERPOLATOR);
35
-
36
-        AnimatorSet set = new AnimatorSet();
33
+        AnimatorSet set;
34
+        if (options.push.hasValue()) {
35
+            set = options.push.getAnimation(view);
36
+        } else {
37
+            set = getDefaultPushAnimation(view);
38
+        }
37
         set.addListener(new AnimatorListenerAdapter() {
39
         set.addListener(new AnimatorListenerAdapter() {
38
             @Override
40
             @Override
39
             public void onAnimationStart(Animator animation) {
41
             public void onAnimationStart(Animator animation) {
47
                 }
49
                 }
48
             }
50
             }
49
         });
51
         });
52
+        set.start();
53
+    }
54
+
55
+    @NonNull
56
+    private AnimatorSet getDefaultPushAnimation(View view) {
57
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1);
58
+        alpha.setInterpolator(DECELERATE_INTERPOLATOR);
59
+
60
+        AnimatorSet set = new AnimatorSet();
50
         ObjectAnimator translationY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, this.translationY, 0);
61
         ObjectAnimator translationY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, this.translationY, 0);
51
         translationY.setInterpolator(DECELERATE_INTERPOLATOR);
62
         translationY.setInterpolator(DECELERATE_INTERPOLATOR);
52
         translationY.setDuration(DURATION);
63
         translationY.setDuration(DURATION);
53
         alpha.setDuration(DURATION);
64
         alpha.setDuration(DURATION);
54
         set.playTogether(translationY, alpha);
65
         set.playTogether(translationY, alpha);
55
-        set.start();
66
+        return set;
56
     }
67
     }
57
 
68
 
58
-    public void animatePop(View view, @Nullable final NavigationAnimationListener animationListener) {
59
-        ObjectAnimator alpha = ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0);
60
-        alpha.setInterpolator(ACCELERATE_INTERPOLATOR);
61
-
62
-        AnimatorSet set = new AnimatorSet();
69
+    public void animatePop(View view, @Nullable final AnimationListener animationListener) {
70
+        AnimatorSet set;
71
+        if (options.pop.hasValue()) {
72
+            set = options.pop.getAnimation(view);
73
+        } else {
74
+            set = getDefaultPopAnimation(view);
75
+        }
63
         set.addListener(new AnimatorListenerAdapter() {
76
         set.addListener(new AnimatorListenerAdapter() {
64
             @Override
77
             @Override
65
             public void onAnimationEnd(Animator animation) {
78
             public void onAnimationEnd(Animator animation) {
68
                 }
81
                 }
69
             }
82
             }
70
         });
83
         });
84
+        set.start();
85
+    }
86
+
87
+    @NonNull
88
+    private AnimatorSet getDefaultPopAnimation(View view) {
89
+        AnimatorSet set = new AnimatorSet();
90
+
91
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0);
92
+        alpha.setInterpolator(ACCELERATE_INTERPOLATOR);
93
+
71
         ObjectAnimator translationY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, 0, this.translationY);
94
         ObjectAnimator translationY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, 0, this.translationY);
72
         translationY.setInterpolator(ACCELERATE_INTERPOLATOR);
95
         translationY.setInterpolator(ACCELERATE_INTERPOLATOR);
73
         translationY.setDuration(DURATION);
96
         translationY.setDuration(DURATION);
74
         alpha.setDuration(DURATION);
97
         alpha.setDuration(DURATION);
75
         set.playTogether(translationY, alpha);
98
         set.playTogether(translationY, alpha);
76
-        set.start();
99
+        return set;
100
+    }
101
+
102
+    public void setOptions(AnimationsOptions options) {
103
+        this.options = options;
77
     }
104
     }
78
 }
105
 }

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

20
 
20
 
21
     private TopBar topBar;
21
     private TopBar topBar;
22
     private View contentView;
22
     private View contentView;
23
+    private ObjectAnimator hideAnimator;
24
+    private ObjectAnimator showAnimator;
23
 
25
 
24
     public TopBarAnimator(TopBar topBar) {
26
     public TopBarAnimator(TopBar topBar) {
25
         this.topBar = topBar;
27
         this.topBar = topBar;
26
     }
28
     }
27
 
29
 
28
-    public TopBarAnimator(TopBar topBar, View contentView) {
29
-        this.topBar = topBar;
30
-        this.contentView = contentView;
31
-    }
32
-
33
     public void show() {
30
     public void show() {
34
         show(-1 * topBar.getMeasuredHeight(), decelerateInterpolator, DURATION_TOPBAR);
31
         show(-1 * topBar.getMeasuredHeight(), decelerateInterpolator, DURATION_TOPBAR);
35
     }
32
     }
36
 
33
 
37
     public void show(float startTranslation, TimeInterpolator interpolator, int duration) {
34
     public void show(float startTranslation, TimeInterpolator interpolator, int duration) {
38
-        ObjectAnimator topbarAnim = ObjectAnimator.ofFloat(topBar, View.TRANSLATION_Y, startTranslation, 0);
39
-        topbarAnim.setInterpolator(interpolator);
40
-        topbarAnim.setDuration(duration);
35
+        showAnimator = ObjectAnimator.ofFloat(topBar, View.TRANSLATION_Y, startTranslation, 0);
36
+        showAnimator.setInterpolator(interpolator);
37
+        showAnimator.setDuration(duration);
41
 
38
 
42
-        topbarAnim.addListener(new AnimatorListenerAdapter() {
39
+        showAnimator.addListener(new AnimatorListenerAdapter() {
43
 
40
 
44
             @Override
41
             @Override
45
             public void onAnimationStart(Animator animation) {
42
             public void onAnimationStart(Animator animation) {
55
                 }
52
                 }
56
             }
53
             }
57
         });
54
         });
58
-        topbarAnim.start();
55
+        showAnimator.start();
59
     }
56
     }
60
 
57
 
61
     public void hide() {
58
     public void hide() {
63
     }
60
     }
64
 
61
 
65
     void hide(float startTranslation, TimeInterpolator interpolator, int duration) {
62
     void hide(float startTranslation, TimeInterpolator interpolator, int duration) {
66
-        ObjectAnimator animator = ObjectAnimator.ofFloat(topBar, View.TRANSLATION_Y, startTranslation, -1 * topBar.getMeasuredHeight());
67
-        animator.setInterpolator(interpolator);
68
-        animator.setDuration(duration);
63
+        hideAnimator = ObjectAnimator.ofFloat(topBar, View.TRANSLATION_Y, startTranslation, -1 * topBar.getMeasuredHeight());
64
+        hideAnimator.setInterpolator(interpolator);
65
+        hideAnimator.setDuration(duration);
69
 
66
 
70
-        animator.addListener(new AnimatorListenerAdapter() {
67
+        hideAnimator.addListener(new AnimatorListenerAdapter() {
71
             @Override
68
             @Override
72
             public void onAnimationEnd(Animator animation) {
69
             public void onAnimationEnd(Animator animation) {
73
                 if (contentView != null) {
70
                 if (contentView != null) {
79
                 topBar.setVisibility(View.GONE);
76
                 topBar.setVisibility(View.GONE);
80
             }
77
             }
81
         });
78
         });
82
-        animator.start();
79
+        hideAnimator.start();
80
+    }
81
+
82
+    public boolean isRunning() {
83
+        return (hideAnimator != null && hideAnimator.isRunning()) || (showAnimator != null && showAnimator.isRunning());
83
     }
84
     }
84
 }
85
 }

+ 49
- 49
lib/android/app/src/main/java/com/reactnativenavigation/anim/ViewAnimationSetBuilder.java View File

8
 
8
 
9
 public class ViewAnimationSetBuilder implements Animation.AnimationListener {
9
 public class ViewAnimationSetBuilder implements Animation.AnimationListener {
10
 
10
 
11
-	private Runnable onComplete;
12
-	private List<View> views = new ArrayList<>();
13
-	private List<Animation> pendingAnimations = new ArrayList<>();
14
-	private boolean started = false;
15
-
16
-	public ViewAnimationSetBuilder withEndListener(Runnable onComplete) {
17
-		this.onComplete = onComplete;
18
-		return this;
19
-	}
20
-
21
-	public ViewAnimationSetBuilder add(View view, Animation animation) {
22
-		views.add(view);
23
-		pendingAnimations.add(animation);
24
-		animation.setAnimationListener(this);
25
-		view.setAnimation(animation);
26
-		return this;
27
-	}
28
-
29
-	public void start() {
30
-		for (Animation animation : pendingAnimations) {
31
-			animation.start();
32
-		}
33
-		started = true;
34
-		if (pendingAnimations.isEmpty()) finish();
35
-	}
36
-
37
-	@Override
38
-	public void onAnimationStart(final Animation animation) {
39
-		// nothing
40
-	}
41
-
42
-	@Override
43
-	public void onAnimationEnd(final Animation animation) {
44
-		pendingAnimations.remove(animation);
45
-		if (started && pendingAnimations.isEmpty()) finish();
46
-	}
47
-
48
-	@Override
49
-	public void onAnimationRepeat(final Animation animation) {
50
-		// nothing
51
-	}
52
-
53
-	private void finish() {
54
-		for (View view : views) {
55
-			view.clearAnimation();
56
-		}
57
-		views.clear();
58
-		if (onComplete != null) onComplete.run();
59
-	}
11
+    private Runnable onComplete;
12
+    private List<View> views = new ArrayList<>();
13
+    private List<Animation> pendingAnimations = new ArrayList<>();
14
+    private boolean started = false;
15
+
16
+    public ViewAnimationSetBuilder withEndListener(Runnable onComplete) {
17
+        this.onComplete = onComplete;
18
+        return this;
19
+    }
20
+
21
+    public ViewAnimationSetBuilder add(View view, Animation animation) {
22
+        views.add(view);
23
+        pendingAnimations.add(animation);
24
+        animation.setAnimationListener(this);
25
+        view.setAnimation(animation);
26
+        return this;
27
+    }
28
+
29
+    public void start() {
30
+        for (Animation animation : pendingAnimations) {
31
+            animation.start();
32
+        }
33
+        started = true;
34
+        if (pendingAnimations.isEmpty()) finish();
35
+    }
36
+
37
+    @Override
38
+    public void onAnimationStart(final Animation animation) {
39
+        // nothing
40
+    }
41
+
42
+    @Override
43
+    public void onAnimationEnd(final Animation animation) {
44
+        pendingAnimations.remove(animation);
45
+        if (started && pendingAnimations.isEmpty()) finish();
46
+    }
47
+
48
+    @Override
49
+    public void onAnimationRepeat(final Animation animation) {
50
+        // nothing
51
+    }
52
+
53
+    private void finish() {
54
+        for (View view : views) {
55
+            view.clearAnimation();
56
+        }
57
+        views.clear();
58
+        if (onComplete != null) onComplete.run();
59
+    }
60
 }
60
 }

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

79
     private void onVerticalScroll(int scrollY, int oldScrollY) {
79
     private void onVerticalScroll(int scrollY, int oldScrollY) {
80
         if (scrollY < 0) return;
80
         if (scrollY < 0) return;
81
         if (!dragStarted) return;
81
         if (!dragStarted) return;
82
+        if (view == null) return;
82
 
83
 
83
         final int scrollDiff = calcScrollDiff(scrollY, oldScrollY, view.getMeasuredHeight());
84
         final int scrollDiff = calcScrollDiff(scrollY, oldScrollY, view.getMeasuredHeight());
84
         final float translationY = view.getTranslationY() - scrollDiff;
85
         final float translationY = view.getTranslationY() - scrollDiff;

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

1
+package com.reactnativenavigation.parse;
2
+
3
+
4
+import android.animation.Animator;
5
+import android.animation.AnimatorSet;
6
+import android.util.Property;
7
+import android.view.View;
8
+
9
+import org.json.JSONObject;
10
+
11
+import java.util.ArrayList;
12
+import java.util.HashSet;
13
+import java.util.Iterator;
14
+import java.util.List;
15
+
16
+public class AnimationOptions {
17
+
18
+    public static AnimationOptions parse(JSONObject json) {
19
+        AnimationOptions options = new AnimationOptions();
20
+        if (json == null) return options;
21
+
22
+        options.hasValue = true;
23
+        for (Iterator<String> it = json.keys(); it.hasNext(); ) {
24
+            String key = it.next();
25
+            options.valueOptions.add(ValueAnimationOptions.parse(json.optJSONObject(key), getAnimProp(key)));
26
+        }
27
+
28
+        return options;
29
+    }
30
+
31
+    private boolean hasValue = false;
32
+
33
+    private HashSet<ValueAnimationOptions> valueOptions = new HashSet<>();
34
+
35
+    void mergeWith(AnimationOptions other) {
36
+        if (other.hasValue()) {
37
+            hasValue = true;
38
+            valueOptions = other.valueOptions;
39
+        }
40
+    }
41
+
42
+    void mergeWithDefault(AnimationOptions defaultOptions) {
43
+        if (defaultOptions.hasValue()) {
44
+            hasValue = true;
45
+            valueOptions = defaultOptions.valueOptions;
46
+        }
47
+    }
48
+
49
+    public boolean hasValue() {
50
+        return hasValue;
51
+    }
52
+
53
+    public AnimatorSet getAnimation(View view) {
54
+        AnimatorSet animationSet = new AnimatorSet();
55
+        List<Animator> animators = new ArrayList<>();
56
+        for (ValueAnimationOptions options: valueOptions) {
57
+            animators.add(options.getAnimation(view));
58
+        }
59
+        animationSet.playTogether(animators);
60
+        return animationSet;
61
+    }
62
+
63
+    private static Property<View, Float> getAnimProp(String key) {
64
+        switch (key) {
65
+            case "y":
66
+                return View.TRANSLATION_Y;
67
+            case "x":
68
+                return View.TRANSLATION_X;
69
+            case "alpha":
70
+                return View.ALPHA;
71
+            case "scaleY":
72
+                return View.SCALE_Y;
73
+            case "scaleX":
74
+                return View.SCALE_X;
75
+            case "rotationX":
76
+                return View.ROTATION_X;
77
+            case "rotationY":
78
+                return View.ROTATION_Y;
79
+            case "rotation":
80
+                return View.ROTATION;
81
+        }
82
+        throw new IllegalArgumentException("This animation is not supported: " + key);
83
+    }
84
+}

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

1
+package com.reactnativenavigation.parse;
2
+
3
+
4
+import org.json.JSONObject;
5
+
6
+public class AnimationsOptions {
7
+
8
+    public static AnimationsOptions parse(JSONObject json) {
9
+        AnimationsOptions options = new AnimationsOptions();
10
+        if (json == null) return options;
11
+
12
+        options.push = AnimationOptions.parse(json.optJSONObject("push"));
13
+        options.pop = AnimationOptions.parse(json.optJSONObject("pop"));
14
+        options.startApp = AnimationOptions.parse(json.optJSONObject("startApp"));
15
+        options.showModal = AnimationOptions.parse(json.optJSONObject("showModal"));
16
+        options.dismissModal = AnimationOptions.parse(json.optJSONObject("dismissModal"));
17
+
18
+        return options;
19
+    }
20
+
21
+    public AnimationOptions push = new AnimationOptions();
22
+    public AnimationOptions pop = new AnimationOptions();
23
+    public AnimationOptions startApp = new AnimationOptions();
24
+    public AnimationOptions showModal = new AnimationOptions();
25
+    public AnimationOptions dismissModal = new AnimationOptions();
26
+
27
+    void mergeWith(AnimationsOptions other) {
28
+        push.mergeWith(other.push);
29
+        pop.mergeWith(other.pop);
30
+        startApp.mergeWith(other.startApp);
31
+        showModal.mergeWith(other.showModal);
32
+        dismissModal.mergeWith(other.dismissModal);
33
+
34
+    }
35
+
36
+    void mergeWithDefault(AnimationsOptions defaultOptions) {
37
+        push.mergeWithDefault(defaultOptions.push);
38
+        pop.mergeWithDefault(defaultOptions.pop);
39
+        startApp.mergeWithDefault(defaultOptions.startApp);
40
+        showModal.mergeWithDefault(defaultOptions.showModal);
41
+        dismissModal.mergeWithDefault(defaultOptions.dismissModal);
42
+    }
43
+}

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

1
+package com.reactnativenavigation.parse;
2
+
3
+import com.reactnativenavigation.parse.params.NullText;
4
+import com.reactnativenavigation.parse.params.Text;
5
+import com.reactnativenavigation.parse.parsers.TextParser;
6
+
7
+import org.json.JSONObject;
8
+
9
+public class BottomTabOptions implements DEFAULT_VALUES {
10
+
11
+    public static BottomTabOptions parse(JSONObject json) {
12
+        BottomTabOptions options = new BottomTabOptions();
13
+        if (json == null) return options;
14
+
15
+        options.title = TextParser.parse(json, "title");
16
+        if (json.has("icon")) {
17
+            options.icon = TextParser.parse(json.optJSONObject("icon"), "uri");
18
+        }
19
+        options.badge = TextParser.parse(json, "badge");
20
+        options.testId = TextParser.parse(json, "testID");
21
+        return options;
22
+    }
23
+
24
+    public Text title = new NullText();
25
+    public Text icon = new NullText();
26
+    public Text testId = new NullText();
27
+    public Text badge = new NullText();
28
+
29
+    void mergeWith(final BottomTabOptions other) {
30
+        if (other.title.hasValue()) {
31
+            title = other.title;
32
+        }
33
+        if (other.icon.hasValue()) {
34
+            icon = other.icon;
35
+        }
36
+        if (other.badge.hasValue()) {
37
+            badge = other.badge;
38
+        }
39
+        if (other.testId.hasValue()) {
40
+            testId = other.testId;
41
+        }
42
+    }
43
+
44
+    void mergeWithDefault(final BottomTabOptions defaultOptions) {
45
+        if (!title.hasValue()) {
46
+            title = defaultOptions.title;
47
+        }
48
+        if (!icon.hasValue()) {
49
+            icon = defaultOptions.icon;
50
+        }
51
+        if (!badge.hasValue()) {
52
+            badge = defaultOptions.badge;
53
+        }
54
+    }
55
+}

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

1
 package com.reactnativenavigation.parse;
1
 package com.reactnativenavigation.parse;
2
 
2
 
3
-
4
-import com.reactnativenavigation.parse.Options.BooleanOptions;
3
+import com.reactnativenavigation.parse.params.Bool;
4
+import com.reactnativenavigation.parse.params.Color;
5
+import com.reactnativenavigation.parse.params.NullBool;
6
+import com.reactnativenavigation.parse.params.NullColor;
7
+import com.reactnativenavigation.parse.params.NullNumber;
8
+import com.reactnativenavigation.parse.params.NullText;
9
+import com.reactnativenavigation.parse.params.Number;
10
+import com.reactnativenavigation.parse.params.Text;
11
+import com.reactnativenavigation.parse.parsers.BoolParser;
12
+import com.reactnativenavigation.parse.parsers.ColorParser;
13
+import com.reactnativenavigation.parse.parsers.NumberParser;
14
+import com.reactnativenavigation.parse.parsers.TextParser;
5
 
15
 
6
 import org.json.JSONObject;
16
 import org.json.JSONObject;
7
 
17
 
11
 		BottomTabsOptions options = new BottomTabsOptions();
21
 		BottomTabsOptions options = new BottomTabsOptions();
12
 		if (json == null) return options;
22
 		if (json == null) return options;
13
 
23
 
14
-		options.currentTabId = TextParser.parse(json, "currentTabId");
15
-		options.currentTabIndex = json.optInt("currentTabIndex", NO_INT_VALUE);
16
-		options.tabBadge = json.optInt("tabBadge", NO_INT_VALUE);
17
-		options.hidden = BooleanOptions.parse(json.optString("hidden"));
18
-		options.animateHide = BooleanOptions.parse(json.optString("animateHide"));
24
+        options.backgroundColor = ColorParser.parse(json, "backgroundColor");
25
+        options.tabColor = ColorParser.parse(json, "tabColor");
26
+        options.selectedTabColor = ColorParser.parse(json, "selectedTabColor");
27
+        options.currentTabId = TextParser.parse(json, "currentTabId");
28
+		options.currentTabIndex = NumberParser.parse(json,"currentTabIndex");
29
+		options.visible = BoolParser.parse(json,"visible");
30
+		options.animate = BoolParser.parse(json,"animate");
31
+        options.testId = TextParser.parse(json, "testID");
19
 
32
 
20
 		return options;
33
 		return options;
21
 	}
34
 	}
22
 
35
 
23
-	int tabBadge = NO_INT_VALUE;
24
-	BooleanOptions hidden = BooleanOptions.False;
25
-	BooleanOptions animateHide = BooleanOptions.False;
26
-	public int currentTabIndex = NO_INT_VALUE;
36
+    public Color backgroundColor = new NullColor();
37
+    public Color tabColor = new NullColor();
38
+    public Color selectedTabColor = new NullColor();
39
+	public Bool visible = new NullBool();
40
+	public Bool animate = new NullBool();
41
+	public Number currentTabIndex = new NullNumber();
27
 	public Text currentTabId = new NullText();
42
 	public Text currentTabId = new NullText();
43
+    public Text testId = new NullText();
28
 
44
 
29
 	void mergeWith(final BottomTabsOptions other) {
45
 	void mergeWith(final BottomTabsOptions other) {
30
 		if (other.currentTabId.hasValue()) {
46
 		if (other.currentTabId.hasValue()) {
31
 			currentTabId = other.currentTabId;
47
 			currentTabId = other.currentTabId;
32
 		}
48
 		}
33
-		if (NO_INT_VALUE != other.currentTabIndex) {
49
+		if (other.currentTabIndex.hasValue()) {
34
             currentTabIndex = other.currentTabIndex;
50
             currentTabIndex = other.currentTabIndex;
35
 		}
51
 		}
36
-		if (NO_INT_VALUE != other.tabBadge) {
37
-			tabBadge = other.tabBadge;
38
-		}
39
-		if (other.hidden != BooleanOptions.NoValue) {
40
-			hidden = other.hidden;
52
+		if (other.visible.hasValue()) {
53
+			visible = other.visible;
41
 		}
54
 		}
42
-		if (other.animateHide != BooleanOptions.NoValue) {
43
-			animateHide = other.animateHide;
55
+		if (other.animate.hasValue()) {
56
+			animate = other.animate;
44
 		}
57
 		}
45
-	}
58
+        if (other.tabColor.hasValue()) {
59
+            tabColor = other.tabColor;
60
+        }
61
+        if (other.selectedTabColor.hasValue()) {
62
+            selectedTabColor = other.selectedTabColor;
63
+        }
64
+        if (other.backgroundColor.hasValue()) {
65
+		    backgroundColor = other.backgroundColor;
66
+        }
67
+    }
46
 
68
 
47
     void mergeWithDefault(final BottomTabsOptions defaultOptions) {
69
     void mergeWithDefault(final BottomTabsOptions defaultOptions) {
48
         if (!currentTabId.hasValue()) {
70
         if (!currentTabId.hasValue()) {
49
             currentTabId = defaultOptions.currentTabId;
71
             currentTabId = defaultOptions.currentTabId;
50
         }
72
         }
51
-        if (NO_INT_VALUE == currentTabIndex) {
73
+        if (!currentTabIndex.hasValue()) {
52
             currentTabIndex = defaultOptions.currentTabIndex;
74
             currentTabIndex = defaultOptions.currentTabIndex;
53
         }
75
         }
54
-        if (NO_INT_VALUE == tabBadge) {
55
-            tabBadge = defaultOptions.tabBadge;
76
+        if (!visible.hasValue()) {
77
+            visible = defaultOptions.visible;
78
+        }
79
+        if (!animate.hasValue()) {
80
+            animate = defaultOptions.animate;
81
+        }
82
+        if (!tabColor.hasValue()) {
83
+            tabColor = defaultOptions.tabColor;
56
         }
84
         }
57
-        if (hidden == BooleanOptions.NoValue) {
58
-            hidden = defaultOptions.hidden;
85
+        if (!selectedTabColor.hasValue()) {
86
+            selectedTabColor = defaultOptions.selectedTabColor;
59
         }
87
         }
60
-        if (animateHide == BooleanOptions.NoValue) {
61
-            animateHide = defaultOptions.animateHide;
88
+        if (!backgroundColor.hasValue()) {
89
+            backgroundColor = defaultOptions.backgroundColor;
62
         }
90
         }
63
     }
91
     }
64
 }
92
 }

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

1
+package com.reactnativenavigation.parse;
2
+
3
+
4
+import com.reactnativenavigation.parse.params.Bool;
5
+import com.reactnativenavigation.parse.params.Color;
6
+import com.reactnativenavigation.parse.params.NullBool;
7
+import com.reactnativenavigation.parse.params.NullColor;
8
+import com.reactnativenavigation.parse.params.NullText;
9
+import com.reactnativenavigation.parse.params.Text;
10
+import com.reactnativenavigation.parse.parsers.BoolParser;
11
+import com.reactnativenavigation.parse.parsers.ColorParser;
12
+import com.reactnativenavigation.parse.parsers.TextParser;
13
+
14
+import org.json.JSONArray;
15
+import org.json.JSONObject;
16
+
17
+import java.util.ArrayList;
18
+
19
+public class FabOptions implements DEFAULT_VALUES {
20
+
21
+    public static FabOptions parse(JSONObject json) {
22
+        FabOptions options = new FabOptions();
23
+        if (json == null) return options;
24
+
25
+        options.id = TextParser.parse(json, "id");
26
+        options.backgroundColor = ColorParser.parse(json, "backgroundColor");
27
+        options.clickColor = ColorParser.parse(json, "clickColor");
28
+        options.rippleColor = ColorParser.parse(json, "rippleColor");
29
+        options.visible = BoolParser.parse(json, "visible");
30
+        if (json.has("icon")) {
31
+            options.icon = TextParser.parse(json.optJSONObject("icon"), "uri");
32
+        }
33
+        if (json.has("actions")) {
34
+            JSONArray fabsArray = json.optJSONArray("actions");
35
+            for (int i = 0; i < fabsArray.length(); i++) {
36
+                options.actionsArray.add(FabOptions.parse(fabsArray.optJSONObject(i)));
37
+            }
38
+        }
39
+        options.alignHorizontally = TextParser.parse(json, "alignHorizontally");
40
+        options.alignVertically = TextParser.parse(json, "alignVertically");
41
+        options.hideOnScroll = BoolParser.parse(json, "hideOnScroll");
42
+        options.size = TextParser.parse(json, "size");
43
+
44
+        return options;
45
+    }
46
+
47
+    public Text id = new NullText();
48
+    public Color backgroundColor = new NullColor();
49
+    public Color clickColor = new NullColor();
50
+    public Color rippleColor = new NullColor();
51
+    public Text icon = new NullText();
52
+    public Bool visible = new NullBool();
53
+    public ArrayList<FabOptions> actionsArray = new ArrayList<>();
54
+    public Text alignHorizontally = new NullText();
55
+    public Text alignVertically = new NullText();
56
+    public Bool hideOnScroll = new NullBool();
57
+    public Text size = new NullText();
58
+
59
+    void mergeWith(final FabOptions other) {
60
+        if (other.id.hasValue()) {
61
+            id = other.id;
62
+        }
63
+        if (other.backgroundColor.hasValue()) {
64
+            backgroundColor = other.backgroundColor;
65
+        }
66
+        if (other.clickColor.hasValue()) {
67
+            clickColor = other.clickColor;
68
+        }
69
+        if (other.rippleColor.hasValue()) {
70
+            rippleColor = other.rippleColor;
71
+        }
72
+        if (other.visible.hasValue()) {
73
+            visible = other.visible;
74
+        }
75
+        if (other.icon.hasValue()) {
76
+            icon = other.icon;
77
+        }
78
+        if (other.actionsArray.size() > 0) {
79
+            actionsArray = other.actionsArray;
80
+        }
81
+        if (other.alignVertically.hasValue()) {
82
+            alignVertically = other.alignVertically;
83
+        }
84
+        if (other.alignHorizontally.hasValue()) {
85
+            alignHorizontally = other.alignHorizontally;
86
+        }
87
+        if (other.hideOnScroll.hasValue()) {
88
+            hideOnScroll = other.hideOnScroll;
89
+        }
90
+        if (other.size.hasValue()) {
91
+            size = other.size;
92
+        }
93
+    }
94
+
95
+    void mergeWithDefault(FabOptions defaultOptions) {
96
+        if (!id.hasValue()) {
97
+            id = defaultOptions.id;
98
+        }
99
+        if (!backgroundColor.hasValue()) {
100
+            backgroundColor = defaultOptions.backgroundColor;
101
+        }
102
+        if (!clickColor.hasValue()) {
103
+            clickColor = defaultOptions.clickColor;
104
+        }
105
+        if (!rippleColor.hasValue()) {
106
+            rippleColor = defaultOptions.rippleColor;
107
+        }
108
+        if (!visible.hasValue()) {
109
+            visible = defaultOptions.visible;
110
+        }
111
+        if (!icon.hasValue()) {
112
+            icon = defaultOptions.icon;
113
+        }
114
+        if (actionsArray.size() == 0) {
115
+            actionsArray = defaultOptions.actionsArray;
116
+        }
117
+        if (!alignHorizontally.hasValue()) {
118
+            alignHorizontally = defaultOptions.alignHorizontally;
119
+        }
120
+        if (!alignVertically.hasValue()) {
121
+            alignVertically = defaultOptions.alignVertically;
122
+        }
123
+        if (!hideOnScroll.hasValue()) {
124
+            hideOnScroll = defaultOptions.hideOnScroll;
125
+        }
126
+        if (!size.hasValue()) {
127
+            size = defaultOptions.size;
128
+        }
129
+    }
130
+}

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

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

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

27
 		this(id, type, new JSONObject(), new ArrayList<>());
27
 		this(id, type, new JSONObject(), new ArrayList<>());
28
 	}
28
 	}
29
 
29
 
30
-	LayoutNode(String id, Type type, JSONObject data, List<LayoutNode> children) {
30
+	public LayoutNode(String id, Type type, JSONObject data, List<LayoutNode> children) {
31
 		this.id = id;
31
 		this.id = id;
32
 		this.type = type;
32
 		this.type = type;
33
 		this.data = data;
33
 		this.data = data;

+ 80
- 27
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.CheckResult;
3
 import android.support.annotation.NonNull;
4
 import android.support.annotation.NonNull;
4
-import android.text.TextUtils;
5
 
5
 
6
 import com.reactnativenavigation.utils.TypefaceLoader;
6
 import com.reactnativenavigation.utils.TypefaceLoader;
7
 
7
 
9
 
9
 
10
 public class Options implements DEFAULT_VALUES {
10
 public class Options implements DEFAULT_VALUES {
11
 
11
 
12
-    public enum BooleanOptions {
13
-		True,
14
-		False,
15
-		NoValue;
16
-
17
-		static BooleanOptions parse(String value) {
18
-			if (!TextUtils.isEmpty(value)) {
19
-				return Boolean.valueOf(value) ? True : False;
20
-			}
21
-			return NoValue;
22
-		}
23
-	}
24
-
25
     @NonNull
12
     @NonNull
26
     public static Options parse(TypefaceLoader typefaceManager, JSONObject json) {
13
     public static Options parse(TypefaceLoader typefaceManager, JSONObject json) {
27
         return parse(typefaceManager, json, new Options());
14
         return parse(typefaceManager, json, new Options());
28
     }
15
     }
29
 
16
 
30
-	@NonNull
31
-	public static Options parse(TypefaceLoader typefaceManager, JSONObject json, @NonNull Options defaultOptions) {
32
-		Options result = new Options();
33
-		if (json == null) return result;
17
+    @NonNull
18
+    public static Options parse(TypefaceLoader typefaceManager, JSONObject json, @NonNull Options defaultOptions) {
19
+        Options result = new Options();
20
+        if (json == null) return result;
34
 
21
 
35
-		result.topBarOptions = TopBarOptions.parse(typefaceManager, json.optJSONObject("topBar"));
36
-		result.topTabsOptions = TopTabsOptions.parse(json.optJSONObject("topTabs"));
22
+        result.topBarOptions = TopBarOptions.parse(typefaceManager, json.optJSONObject("topBar"));
23
+        result.topTabsOptions = TopTabsOptions.parse(json.optJSONObject("topTabs"));
37
         result.topTabOptions = TopTabOptions.parse(typefaceManager, json.optJSONObject("topTab"));
24
         result.topTabOptions = TopTabOptions.parse(typefaceManager, json.optJSONObject("topTab"));
38
-		result.bottomTabsOptions = BottomTabsOptions.parse(json.optJSONObject("bottomTabs"));
25
+        result.bottomTabOptions = BottomTabOptions.parse(json.optJSONObject("bottomTab"));
26
+        result.bottomTabsOptions = BottomTabsOptions.parse(json.optJSONObject("bottomTabs"));
39
         result.overlayOptions = OverlayOptions.parse(json.optJSONObject("overlay"));
27
         result.overlayOptions = OverlayOptions.parse(json.optJSONObject("overlay"));
28
+        result.fabOptions = FabOptions.parse(json.optJSONObject("fab"));
29
+        result.animationsOptions = AnimationsOptions.parse(json.optJSONObject("animations"));
30
+        result.sideMenuRootOptions = SideMenuRootOptions.parse(json.optJSONObject("sideMenu"));
40
 
31
 
41
-		return result.withDefaultOptions(defaultOptions);
42
-	}
32
+        return result.withDefaultOptions(defaultOptions);
33
+    }
43
 
34
 
44
     @NonNull public TopBarOptions topBarOptions = new TopBarOptions();
35
     @NonNull public TopBarOptions topBarOptions = new TopBarOptions();
45
     @NonNull public TopTabsOptions topTabsOptions = new TopTabsOptions();
36
     @NonNull public TopTabsOptions topTabsOptions = new TopTabsOptions();
46
     @NonNull public TopTabOptions topTabOptions = new TopTabOptions();
37
     @NonNull public TopTabOptions topTabOptions = new TopTabOptions();
38
+    @NonNull public BottomTabOptions bottomTabOptions = new BottomTabOptions();
47
     @NonNull public BottomTabsOptions bottomTabsOptions = new BottomTabsOptions();
39
     @NonNull public BottomTabsOptions bottomTabsOptions = new BottomTabsOptions();
48
     @NonNull public OverlayOptions overlayOptions = new OverlayOptions();
40
     @NonNull public OverlayOptions overlayOptions = new OverlayOptions();
41
+    @NonNull public FabOptions fabOptions = new FabOptions();
42
+    @NonNull public AnimationsOptions animationsOptions = new AnimationsOptions();
43
+    @NonNull public SideMenuRootOptions sideMenuRootOptions = new SideMenuRootOptions();
49
 
44
 
50
     void setTopTabIndex(int i) {
45
     void setTopTabIndex(int i) {
51
         topTabOptions.tabIndex = i;
46
         topTabOptions.tabIndex = i;
52
     }
47
     }
53
 
48
 
54
-	public void mergeWith(final Options other) {
55
-        topBarOptions.mergeWith(other.topBarOptions);
56
-        topTabsOptions.mergeWith(other.topTabsOptions);
57
-        bottomTabsOptions.mergeWith(other.bottomTabsOptions);
49
+    @CheckResult
50
+    public Options copy() {
51
+        Options result = new Options();
52
+        result.topBarOptions.mergeWith(topBarOptions);
53
+        result.topTabsOptions.mergeWith(topTabsOptions);
54
+        result.topTabOptions.mergeWith(topTabOptions);
55
+        result.bottomTabOptions.mergeWith(bottomTabOptions);
56
+        result.bottomTabsOptions.mergeWith(bottomTabsOptions);
57
+        result.overlayOptions = overlayOptions;
58
+        result.fabOptions.mergeWith(fabOptions);
59
+        result.animationsOptions.mergeWith(animationsOptions);
60
+        result.sideMenuRootOptions.mergeWith(sideMenuRootOptions);
61
+        return result;
62
+    }
63
+
64
+    @CheckResult
65
+	public Options mergeWith(final Options other) {
66
+        Options result = copy();
67
+        result.topBarOptions.mergeWith(other.topBarOptions);
68
+        result.topTabsOptions.mergeWith(other.topTabsOptions);
69
+        result.topTabOptions.mergeWith(other.topTabOptions);
70
+        result.bottomTabOptions.mergeWith(other.bottomTabOptions);
71
+        result.bottomTabsOptions.mergeWith(other.bottomTabsOptions);
72
+        result.fabOptions.mergeWith(other.fabOptions);
73
+        result.animationsOptions.mergeWith(other.animationsOptions);
74
+        result.sideMenuRootOptions.mergeWith(other.sideMenuRootOptions);
75
+        return result;
58
     }
76
     }
59
 
77
 
60
     Options withDefaultOptions(final Options other) {
78
     Options withDefaultOptions(final Options other) {
61
         topBarOptions.mergeWithDefault(other.topBarOptions);
79
         topBarOptions.mergeWithDefault(other.topBarOptions);
80
+        topTabOptions.mergeWithDefault(other.topTabOptions);
62
         topTabsOptions.mergeWithDefault(other.topTabsOptions);
81
         topTabsOptions.mergeWithDefault(other.topTabsOptions);
82
+        bottomTabOptions.mergeWithDefault(other.bottomTabOptions);
63
         bottomTabsOptions.mergeWithDefault(other.bottomTabsOptions);
83
         bottomTabsOptions.mergeWithDefault(other.bottomTabsOptions);
84
+        fabOptions.mergeWithDefault(other.fabOptions);
85
+        animationsOptions.mergeWithDefault(other.animationsOptions);
86
+        sideMenuRootOptions.mergeWithDefault(other.sideMenuRootOptions);
87
+        return this;
88
+    }
89
+
90
+    public Options clearTopBarOptions() {
91
+        topBarOptions = new TopBarOptions();
92
+        return this;
93
+    }
94
+
95
+    public Options clearBottomTabsOptions() {
96
+        bottomTabsOptions = new BottomTabsOptions();
97
+        return this;
98
+    }
99
+
100
+    public Options clearTopTabOptions() {
101
+        topTabOptions = new TopTabOptions();
102
+        return this;
103
+    }
104
+
105
+    public Options clearTopTabsOptions() {
106
+        topTabsOptions = new TopTabsOptions();
107
+        return this;
108
+    }
109
+
110
+    public Options clearBottomTabOptions() {
111
+        bottomTabOptions = new BottomTabOptions();
112
+        return this;
113
+    }
114
+
115
+    public Options clearSideMenuOptions() {
116
+        sideMenuRootOptions = new SideMenuRootOptions();
64
         return this;
117
         return this;
65
     }
118
     }
66
 }
119
 }

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

1
 package com.reactnativenavigation.parse;
1
 package com.reactnativenavigation.parse;
2
 
2
 
3
+import com.reactnativenavigation.parse.params.Bool;
4
+import com.reactnativenavigation.parse.params.NullBool;
5
+import com.reactnativenavigation.parse.parsers.BoolParser;
6
+
3
 import org.json.JSONObject;
7
 import org.json.JSONObject;
4
 
8
 
5
 public class OverlayOptions {
9
 public class OverlayOptions {
6
-    public Options.BooleanOptions interceptTouchOutside = Options.BooleanOptions.False;
10
+    public Bool interceptTouchOutside = new NullBool();
7
 
11
 
8
     public static OverlayOptions parse(JSONObject json) {
12
     public static OverlayOptions parse(JSONObject json) {
9
         OverlayOptions options = new OverlayOptions();
13
         OverlayOptions options = new OverlayOptions();
10
         if (json == null) return options;
14
         if (json == null) return options;
11
 
15
 
12
-        options.interceptTouchOutside = Options.BooleanOptions.parse(json.optString("interceptTouchOutside", ""));
16
+        options.interceptTouchOutside = BoolParser.parse(json,"interceptTouchOutside");
13
         return options;
17
         return options;
14
     }
18
     }
15
 }
19
 }

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

1
+package com.reactnativenavigation.parse;
2
+
3
+import com.reactnativenavigation.parse.params.Bool;
4
+import com.reactnativenavigation.parse.params.NullBool;
5
+import com.reactnativenavigation.parse.parsers.BoolParser;
6
+
7
+import org.json.JSONObject;
8
+
9
+public class SideMenuOptions {
10
+    public Bool visible = new NullBool();
11
+
12
+    public static SideMenuOptions parse(JSONObject json) {
13
+        SideMenuOptions options = new SideMenuOptions();
14
+        if (json == null) return options;
15
+
16
+        options.visible = BoolParser.parse(json, "visible");
17
+        return options;
18
+    }
19
+
20
+    public void mergeWith(SideMenuOptions other) {
21
+        if (other.visible.hasValue()) {
22
+            visible = other.visible;
23
+        }
24
+    }
25
+}

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

1
+package com.reactnativenavigation.parse;
2
+
3
+import org.json.JSONObject;
4
+
5
+public class SideMenuRootOptions {
6
+    public SideMenuOptions left = new SideMenuOptions();
7
+    public SideMenuOptions right = new SideMenuOptions();
8
+
9
+    public static SideMenuRootOptions parse(JSONObject json) {
10
+        SideMenuRootOptions options = new SideMenuRootOptions();
11
+        if (json == null) return options;
12
+
13
+        options.left = SideMenuOptions.parse(json.optJSONObject("left"));
14
+        options.right = SideMenuOptions.parse(json.optJSONObject("right"));
15
+
16
+        return options;
17
+    }
18
+
19
+    public void mergeWith(SideMenuRootOptions other) {
20
+        left.mergeWith(other.left);
21
+        right.mergeWith(other.right);
22
+    }
23
+
24
+    public void mergeWithDefault(SideMenuRootOptions sideMenuRootOptions) {
25
+
26
+    }
27
+}

+ 0
- 7
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
-}

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

4
 import android.graphics.Typeface;
4
 import android.graphics.Typeface;
5
 import android.support.annotation.Nullable;
5
 import android.support.annotation.Nullable;
6
 
6
 
7
+import com.reactnativenavigation.parse.params.Bool;
8
+import com.reactnativenavigation.parse.params.Button;
9
+import com.reactnativenavigation.parse.params.Color;
10
+import com.reactnativenavigation.parse.params.Fraction;
11
+import com.reactnativenavigation.parse.params.NullBool;
12
+import com.reactnativenavigation.parse.params.NullColor;
13
+import com.reactnativenavigation.parse.params.NullFraction;
14
+import com.reactnativenavigation.parse.params.NullText;
15
+import com.reactnativenavigation.parse.params.Text;
16
+import com.reactnativenavigation.parse.parsers.BoolParser;
17
+import com.reactnativenavigation.parse.parsers.ColorParser;
18
+import com.reactnativenavigation.parse.parsers.FractionParser;
19
+import com.reactnativenavigation.parse.parsers.TextParser;
7
 import com.reactnativenavigation.utils.TypefaceLoader;
20
 import com.reactnativenavigation.utils.TypefaceLoader;
8
 
21
 
9
 import org.json.JSONObject;
22
 import org.json.JSONObject;
21
         options.textColor = ColorParser.parse(json, "textColor");
34
         options.textColor = ColorParser.parse(json, "textColor");
22
         options.textFontSize = FractionParser.parse(json, "textFontSize");
35
         options.textFontSize = FractionParser.parse(json, "textFontSize");
23
         options.textFontFamily = typefaceManager.getTypeFace(json.optString("textFontFamily", ""));
36
         options.textFontFamily = typefaceManager.getTypeFace(json.optString("textFontFamily", ""));
24
-        options.hidden = Options.BooleanOptions.parse(json.optString("hidden"));
25
-        options.animateHide = Options.BooleanOptions.parse(json.optString("animateHide"));
26
-        options.hideOnScroll = Options.BooleanOptions.parse(json.optString("hideOnScroll"));
27
-        options.drawBehind = Options.BooleanOptions.parse(json.optString("drawBehind"));
37
+        options.visible = BoolParser.parse(json, "visible");
38
+        options.animate = BoolParser.parse(json,"animate");
39
+        options.hideOnScroll = BoolParser.parse(json,"hideOnScroll");
40
+        options.drawBehind = BoolParser.parse(json,"drawBehind");
28
         options.rightButtons = Button.parseJsonArray(json.optJSONArray("rightButtons"));
41
         options.rightButtons = Button.parseJsonArray(json.optJSONArray("rightButtons"));
29
         options.leftButtons = Button.parseJsonArray(json.optJSONArray("leftButtons"));
42
         options.leftButtons = Button.parseJsonArray(json.optJSONArray("leftButtons"));
43
+        options.testId = TextParser.parse(json, "testID");
30
 
44
 
31
         return options;
45
         return options;
32
     }
46
     }
33
 
47
 
34
     public Text title = new NullText();
48
     public Text title = new NullText();
49
+    public Text testId = new NullText();
35
     public Color backgroundColor = new NullColor();
50
     public Color backgroundColor = new NullColor();
36
     public Color textColor = new NullColor();
51
     public Color textColor = new NullColor();
37
     public Fraction textFontSize = new NullFraction();
52
     public Fraction textFontSize = new NullFraction();
38
     @Nullable public Typeface textFontFamily;
53
     @Nullable public Typeface textFontFamily;
39
-    public Options.BooleanOptions hidden = Options.BooleanOptions.NoValue;
40
-    public Options.BooleanOptions animateHide = Options.BooleanOptions.NoValue;
41
-    public Options.BooleanOptions hideOnScroll = Options.BooleanOptions.NoValue;
42
-    public Options.BooleanOptions drawBehind = Options.BooleanOptions.NoValue;
54
+    public Bool visible = new NullBool();
55
+    public Bool animate = new NullBool();
56
+    public Bool hideOnScroll = new NullBool();
57
+    public Bool drawBehind = new NullBool();
43
     public ArrayList<Button> leftButtons;
58
     public ArrayList<Button> leftButtons;
44
     public ArrayList<Button> rightButtons;
59
     public ArrayList<Button> rightButtons;
45
 
60
 
46
     void mergeWith(final TopBarOptions other) {
61
     void mergeWith(final TopBarOptions other) {
47
-        if (other.title != null) title = other.title;
62
+        if (other.title.hasValue())
63
+            title = other.title;
48
         if (other.backgroundColor.hasValue())
64
         if (other.backgroundColor.hasValue())
49
             backgroundColor = other.backgroundColor;
65
             backgroundColor = other.backgroundColor;
50
         if (other.textColor.hasValue())
66
         if (other.textColor.hasValue())
53
             textFontSize = other.textFontSize;
69
             textFontSize = other.textFontSize;
54
         if (other.textFontFamily != null)
70
         if (other.textFontFamily != null)
55
             textFontFamily = other.textFontFamily;
71
             textFontFamily = other.textFontFamily;
56
-        if (other.hidden != Options.BooleanOptions.NoValue) {
57
-            hidden = other.hidden;
72
+        if (other.visible.hasValue()) {
73
+            visible = other.visible;
58
         }
74
         }
59
-        if (other.animateHide != Options.BooleanOptions.NoValue) {
60
-            animateHide = other.animateHide;
75
+        if (other.animate.hasValue()) {
76
+            animate = other.animate;
61
         }
77
         }
62
-        if (other.hideOnScroll != Options.BooleanOptions.NoValue) {
78
+        if (other.hideOnScroll.hasValue()) {
63
             hideOnScroll = other.hideOnScroll;
79
             hideOnScroll = other.hideOnScroll;
64
         }
80
         }
65
-        if (other.drawBehind != Options.BooleanOptions.NoValue) {
81
+        if (other.drawBehind.hasValue()) {
66
             drawBehind = other.drawBehind;
82
             drawBehind = other.drawBehind;
67
         }
83
         }
68
         if (other.leftButtons != null)
84
         if (other.leftButtons != null)
82
             textFontSize = defaultOptions.textFontSize;
98
             textFontSize = defaultOptions.textFontSize;
83
         if (textFontFamily == null)
99
         if (textFontFamily == null)
84
             textFontFamily = defaultOptions.textFontFamily;
100
             textFontFamily = defaultOptions.textFontFamily;
85
-        if (hidden == Options.BooleanOptions.NoValue)
86
-            hidden = defaultOptions.hidden;
87
-        if (animateHide == Options.BooleanOptions.NoValue)
88
-            animateHide = defaultOptions.animateHide;
89
-        if (hideOnScroll == Options.BooleanOptions.NoValue)
101
+        if (!visible.hasValue())
102
+            visible = defaultOptions.visible;
103
+        if (!animate.hasValue())
104
+            animate = defaultOptions.animate;
105
+        if (!hideOnScroll.hasValue())
90
             hideOnScroll = defaultOptions.hideOnScroll;
106
             hideOnScroll = defaultOptions.hideOnScroll;
91
-        if (drawBehind == Options.BooleanOptions.NoValue)
107
+        if (!drawBehind.hasValue())
92
             drawBehind = defaultOptions.drawBehind;
108
             drawBehind = defaultOptions.drawBehind;
93
         if (leftButtons == null)
109
         if (leftButtons == null)
94
             leftButtons = defaultOptions.leftButtons;
110
             leftButtons = defaultOptions.leftButtons;

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

3
 import android.graphics.Typeface;
3
 import android.graphics.Typeface;
4
 import android.support.annotation.Nullable;
4
 import android.support.annotation.Nullable;
5
 
5
 
6
+import com.reactnativenavigation.parse.params.NullText;
7
+import com.reactnativenavigation.parse.params.Text;
8
+import com.reactnativenavigation.parse.parsers.TextParser;
6
 import com.reactnativenavigation.utils.TypefaceLoader;
9
 import com.reactnativenavigation.utils.TypefaceLoader;
7
 
10
 
8
 import org.json.JSONObject;
11
 import org.json.JSONObject;
21
         return result;
24
         return result;
22
     }
25
     }
23
 
26
 
24
-    void mergeWith(TopTabOptions topTabsOptions) {
25
-
27
+    void mergeWith(TopTabOptions other) {
28
+        if (other.title.hasValue()) title = other.title;
29
+        if (other.fontFamily != null) fontFamily = other.fontFamily;
30
+        if (other.tabIndex >= 0) tabIndex = other.tabIndex;
26
     }
31
     }
27
 
32
 
28
-    void mergeWithDefault(TopTabOptions topTabsOptions) {
29
-
33
+    void mergeWithDefault(TopTabOptions other) {
34
+        if (fontFamily == null) fontFamily = other.fontFamily;
30
     }
35
     }
31
 }
36
 }

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

3
 import android.support.annotation.NonNull;
3
 import android.support.annotation.NonNull;
4
 import android.support.annotation.Nullable;
4
 import android.support.annotation.Nullable;
5
 
5
 
6
+import com.reactnativenavigation.parse.params.Bool;
7
+import com.reactnativenavigation.parse.params.Color;
8
+import com.reactnativenavigation.parse.params.NullBool;
9
+import com.reactnativenavigation.parse.params.NullColor;
10
+import com.reactnativenavigation.parse.params.NullNumber;
11
+import com.reactnativenavigation.parse.params.Number;
12
+import com.reactnativenavigation.parse.parsers.BoolParser;
13
+import com.reactnativenavigation.parse.parsers.ColorParser;
14
+import com.reactnativenavigation.parse.parsers.NumberParser;
15
+
6
 import org.json.JSONObject;
16
 import org.json.JSONObject;
7
 
17
 
8
 public class TopTabsOptions implements DEFAULT_VALUES {
18
 public class TopTabsOptions implements DEFAULT_VALUES {
10
     @NonNull public Color selectedTabColor = new NullColor();
20
     @NonNull public Color selectedTabColor = new NullColor();
11
     @NonNull public Color unselectedTabColor = new NullColor();
21
     @NonNull public Color unselectedTabColor = new NullColor();
12
     @NonNull public Number fontSize = new NullNumber();
22
     @NonNull public Number fontSize = new NullNumber();
23
+    @NonNull public Bool visible = new NullBool();
13
 
24
 
14
     public static TopTabsOptions parse(@Nullable JSONObject json) {
25
     public static TopTabsOptions parse(@Nullable JSONObject json) {
15
         TopTabsOptions result = new TopTabsOptions();
26
         TopTabsOptions result = new TopTabsOptions();
16
         if (json == null) return result;
27
         if (json == null) return result;
17
-        result.selectedTabColor = ColorParser.parse(json, "selectedTabColor");
28
+        result.selectedTabColor = ColorParser.parse(json, "selectedColor");
18
         result.unselectedTabColor = ColorParser.parse(json, "unselectedTabColor");
29
         result.unselectedTabColor = ColorParser.parse(json, "unselectedTabColor");
19
         result.fontSize = NumberParser.parse(json, "fontSize");
30
         result.fontSize = NumberParser.parse(json, "fontSize");
31
+        result.visible = BoolParser.parse(json, "visible");
20
         return result;
32
         return result;
21
     }
33
     }
22
 
34
 
24
         if (other.selectedTabColor.hasValue()) selectedTabColor = other.selectedTabColor;
36
         if (other.selectedTabColor.hasValue()) selectedTabColor = other.selectedTabColor;
25
         if (other.unselectedTabColor.hasValue()) unselectedTabColor = other.unselectedTabColor;
37
         if (other.unselectedTabColor.hasValue()) unselectedTabColor = other.unselectedTabColor;
26
         if (other.fontSize.hasValue()) fontSize = other.fontSize;
38
         if (other.fontSize.hasValue()) fontSize = other.fontSize;
39
+        if (other.visible.hasValue()) visible = other.visible;
27
     }
40
     }
28
 
41
 
29
     void mergeWithDefault(TopTabsOptions defaultOptions) {
42
     void mergeWithDefault(TopTabsOptions defaultOptions) {
30
         if (!selectedTabColor.hasValue()) selectedTabColor = defaultOptions.selectedTabColor;
43
         if (!selectedTabColor.hasValue()) selectedTabColor = defaultOptions.selectedTabColor;
31
         if (!unselectedTabColor.hasValue()) unselectedTabColor = defaultOptions.unselectedTabColor;
44
         if (!unselectedTabColor.hasValue()) unselectedTabColor = defaultOptions.unselectedTabColor;
32
         if (!fontSize.hasValue()) fontSize = defaultOptions.fontSize;
45
         if (!fontSize.hasValue()) fontSize = defaultOptions.fontSize;
46
+        if (!visible.hasValue()) visible = defaultOptions.visible;
33
     }
47
     }
34
 }
48
 }

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

1
+package com.reactnativenavigation.parse;
2
+
3
+
4
+import android.animation.Animator;
5
+import android.animation.ObjectAnimator;
6
+import android.util.Property;
7
+import android.view.View;
8
+
9
+import com.reactnativenavigation.parse.params.FloatParam;
10
+import com.reactnativenavigation.parse.params.Interpolation;
11
+import com.reactnativenavigation.parse.params.NullFloatParam;
12
+import com.reactnativenavigation.parse.params.NullNumber;
13
+import com.reactnativenavigation.parse.params.Number;
14
+import com.reactnativenavigation.parse.parsers.FloatParser;
15
+import com.reactnativenavigation.parse.parsers.InterpolationParser;
16
+import com.reactnativenavigation.parse.parsers.NumberParser;
17
+
18
+import org.json.JSONObject;
19
+
20
+public class ValueAnimationOptions implements DEFAULT_VALUES {
21
+
22
+    public static ValueAnimationOptions parse(JSONObject json, Property<View, Float> property) {
23
+        ValueAnimationOptions options = new ValueAnimationOptions();
24
+
25
+        options.animProp = property;
26
+        options.from = FloatParser.parse(json, "from");
27
+        options.to = FloatParser.parse(json, "to");
28
+        options.duration = NumberParser.parse(json, "duration");
29
+        options.startDelay = NumberParser.parse(json, "startDelay");
30
+        options.interpolation = InterpolationParser.parse(json, "interpolation");
31
+
32
+        return options;
33
+    }
34
+
35
+    private Property<View, Float> animProp;
36
+
37
+    private FloatParam from = new NullFloatParam();
38
+    private FloatParam to = new NullFloatParam();
39
+    private Number duration = new NullNumber();
40
+    private Number startDelay = new NullNumber();
41
+    private Interpolation interpolation = Interpolation.NO_VALUE;
42
+
43
+    Animator getAnimation(View view) {
44
+        if (!this.from.hasValue() || !this.to.hasValue())
45
+            throw new IllegalArgumentException("Params 'from' and 'to' are mandatory");
46
+        ObjectAnimator animator = ObjectAnimator.ofFloat(view, animProp, this.from.get(), this.to.get());
47
+        animator.setInterpolator(this.interpolation.getInterpolator());
48
+        if (this.duration.hasValue())
49
+            animator.setDuration(this.duration.get());
50
+        if (this.startDelay.hasValue())
51
+            animator.setStartDelay(this.startDelay.get());
52
+        return animator;
53
+    }
54
+
55
+    @Override
56
+    public boolean equals(Object o) {
57
+        if (this == o) return true;
58
+        if (o == null || getClass() != o.getClass()) return false;
59
+
60
+        ValueAnimationOptions options = (ValueAnimationOptions) o;
61
+
62
+        return animProp.equals(options.animProp);
63
+    }
64
+
65
+    @Override
66
+    public int hashCode() {
67
+        return animProp.hashCode();
68
+    }
69
+}

+ 23
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Bool.java View File

1
+package com.reactnativenavigation.parse.params;
2
+
3
+public class Bool extends Param<Boolean> {
4
+    public Bool(Boolean value) {
5
+        super(value);
6
+    }
7
+
8
+    public boolean isFalseOrUndefined() {
9
+        return !hasValue() || !get();
10
+    }
11
+
12
+    public boolean isTrueOrUndefined() {
13
+        return !hasValue() || get();
14
+    }
15
+
16
+    public boolean isTrue() {
17
+        return hasValue() && get();
18
+    }
19
+
20
+    public boolean isFalse() {
21
+        return hasValue() && !get();
22
+    }
23
+}

lib/android/app/src/main/java/com/reactnativenavigation/parse/Button.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java View File

1
-package com.reactnativenavigation.parse;
1
+package com.reactnativenavigation.parse.params;
2
 
2
 
3
 import android.support.annotation.ColorInt;
3
 import android.support.annotation.ColorInt;
4
 import android.view.MenuItem;
4
 import android.view.MenuItem;
5
 
5
 
6
+import com.reactnativenavigation.parse.parsers.BoolParser;
7
+import com.reactnativenavigation.parse.parsers.TextParser;
8
+
6
 import org.json.JSONArray;
9
 import org.json.JSONArray;
7
 import org.json.JSONObject;
10
 import org.json.JSONObject;
8
 
11
 
12
 
15
 
13
 public class Button {
16
 public class Button {
14
 	public String id;
17
 	public String id;
15
-	public Text title;
16
-	public Options.BooleanOptions disabled;
17
-	public Options.BooleanOptions disableIconTint;
18
+	public Text title = new NullText();
19
+	public Bool enabled = new NullBool();
20
+	public Bool disableIconTint = new NullBool();
18
 	public int showAsAction;
21
 	public int showAsAction;
19
 	@ColorInt public int buttonColor;
22
 	@ColorInt public int buttonColor;
20
 	public int buttonFontSize;
23
 	public int buttonFontSize;
21
-	public Text buttonFontWeight;
22
-	public Text icon;
24
+	private Text buttonFontWeight = new NullText();
25
+	public Text icon = new NullText();
26
+	public Text testId = new NullText();
23
 
27
 
24
 	private static Button parseJson(JSONObject json)  {
28
 	private static Button parseJson(JSONObject json)  {
25
 		Button button = new Button();
29
 		Button button = new Button();
26
 		button.id = json.optString("id");
30
 		button.id = json.optString("id");
27
 		button.title = TextParser.parse(json, "title");
31
 		button.title = TextParser.parse(json, "title");
28
-		button.disabled = Options.BooleanOptions.parse(json.optString("disabled", ""));
29
-		button.disableIconTint = Options.BooleanOptions.parse(json.optString("disableIconTint", ""));
32
+		button.enabled = BoolParser.parse(json,"enabled");
33
+		button.disableIconTint = BoolParser.parse(json,"disableIconTint");
30
 		button.showAsAction = parseShowAsAction(json);
34
 		button.showAsAction = parseShowAsAction(json);
31
 		button.buttonColor = json.optInt("buttonColor", NO_INT_VALUE);
35
 		button.buttonColor = json.optInt("buttonColor", NO_INT_VALUE);
32
 		button.buttonFontSize = json.optInt("buttonFontSize", NO_INT_VALUE);
36
 		button.buttonFontSize = json.optInt("buttonFontSize", NO_INT_VALUE);
33
 		button.buttonFontWeight = TextParser.parse(json, "buttonFontWeight");
37
 		button.buttonFontWeight = TextParser.parse(json, "buttonFontWeight");
38
+        button.testId = TextParser.parse(json, "testID");
34
 
39
 
35
 		if (json.has("icon")) {
40
 		if (json.has("icon")) {
36
 			button.icon = TextParser.parse(json.optJSONObject("icon"), "uri");
41
 			button.icon = TextParser.parse(json.optJSONObject("icon"), "uri");
39
 		return button;
44
 		return button;
40
 	}
45
 	}
41
 
46
 
42
-	static ArrayList<Button> parseJsonArray(JSONArray jsonArray) {
47
+	public static ArrayList<Button> parseJsonArray(JSONArray jsonArray) {
43
 		ArrayList<Button> buttons = new ArrayList<>();
48
 		ArrayList<Button> buttons = new ArrayList<>();
44
 
49
 
45
 		if (jsonArray == null) {
50
 		if (jsonArray == null) {

lib/android/app/src/main/java/com/reactnativenavigation/parse/Color.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Color.java View File

1
-package com.reactnativenavigation.parse;
1
+package com.reactnativenavigation.parse.params;
2
 
2
 
3
 import android.support.annotation.ColorInt;
3
 import android.support.annotation.ColorInt;
4
 
4
 

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

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

lib/android/app/src/main/java/com/reactnativenavigation/parse/Fraction.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Fraction.java View File

1
-package com.reactnativenavigation.parse;
1
+package com.reactnativenavigation.parse.params;
2
 
2
 
3
 public class Fraction extends Param<Float> {
3
 public class Fraction extends Param<Float> {
4
     public Fraction(float value) {
4
     public Fraction(float value) {

+ 26
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Interpolation.java View File

1
+package com.reactnativenavigation.parse.params;
2
+
3
+
4
+import android.animation.TimeInterpolator;
5
+import android.view.animation.AccelerateDecelerateInterpolator;
6
+import android.view.animation.AccelerateInterpolator;
7
+import android.view.animation.DecelerateInterpolator;
8
+
9
+public enum Interpolation {
10
+    ACCELERATING,
11
+    DECELERATING,
12
+    DEFAULT,
13
+    NO_VALUE;
14
+
15
+    public TimeInterpolator getInterpolator() {
16
+        switch (this) {
17
+            case ACCELERATING:
18
+                return new AccelerateInterpolator();
19
+            case DECELERATING:
20
+                return new DecelerateInterpolator();
21
+            case DEFAULT:
22
+                return new AccelerateDecelerateInterpolator();
23
+        }
24
+        return null;
25
+    }
26
+}

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

1
+package com.reactnativenavigation.parse.params;
2
+
3
+public class NullBool extends Bool {
4
+    public NullBool() {
5
+        super(null);
6
+    }
7
+}

lib/android/app/src/main/java/com/reactnativenavigation/parse/NullColor.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/params/NullColor.java View File

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

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

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

lib/android/app/src/main/java/com/reactnativenavigation/parse/NullFraction.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/params/NullFraction.java View File

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

lib/android/app/src/main/java/com/reactnativenavigation/parse/NullNumber.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/params/NullNumber.java View File

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

lib/android/app/src/main/java/com/reactnativenavigation/parse/NullText.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/params/NullText.java View File

1
-package com.reactnativenavigation.parse;
1
+package com.reactnativenavigation.parse.params;
2
 
2
 
3
 public class NullText extends Text {
3
 public class NullText extends Text {
4
     public NullText() {
4
     public NullText() {

lib/android/app/src/main/java/com/reactnativenavigation/parse/Number.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Number.java View File

1
-package com.reactnativenavigation.parse;
1
+package com.reactnativenavigation.parse.params;
2
 
2
 
3
 public class Number extends Param<Integer> {
3
 public class Number extends Param<Integer> {
4
 
4
 

lib/android/app/src/main/java/com/reactnativenavigation/parse/Param.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Param.java View File

1
-package com.reactnativenavigation.parse;
1
+package com.reactnativenavigation.parse.params;
2
 
2
 
3
 public abstract class Param<T> {
3
 public abstract class Param<T> {
4
     protected T value;
4
     protected T value;

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

1
+package com.reactnativenavigation.parse.params;
2
+
3
+public class Text extends Param<String> {
4
+    public Text(String value) {
5
+        super(value);
6
+    }
7
+
8
+    @Override
9
+    public String toString() {
10
+        return hasValue() ? value : "No Value";
11
+    }
12
+}

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

1
+package com.reactnativenavigation.parse.parsers;
2
+
3
+import com.reactnativenavigation.parse.params.Bool;
4
+import com.reactnativenavigation.parse.params.NullBool;
5
+
6
+import org.json.JSONObject;
7
+
8
+public class BoolParser {
9
+    public static Bool parse(JSONObject json, String bool) {
10
+        return json.has(bool) ? new Bool(json.optBoolean(bool)) : new NullBool();
11
+    }
12
+}

lib/android/app/src/main/java/com/reactnativenavigation/parse/ColorParser.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/ColorParser.java View File

1
-package com.reactnativenavigation.parse;
1
+package com.reactnativenavigation.parse.parsers;
2
+
3
+import com.reactnativenavigation.parse.params.Color;
4
+import com.reactnativenavigation.parse.params.NullColor;
2
 
5
 
3
 import org.json.JSONObject;
6
 import org.json.JSONObject;
4
 
7
 

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

1
+package com.reactnativenavigation.parse.parsers;
2
+
3
+import com.reactnativenavigation.parse.params.FloatParam;
4
+import com.reactnativenavigation.parse.params.NullFloatParam;
5
+import com.reactnativenavigation.parse.params.NullNumber;
6
+import com.reactnativenavigation.parse.params.Number;
7
+
8
+import org.json.JSONObject;
9
+
10
+public class FloatParser {
11
+    public static FloatParam parse(JSONObject json, String number) {
12
+        return json.has(number) ? new FloatParam((float) json.optDouble(number)) : new NullFloatParam();
13
+    }
14
+}

lib/android/app/src/main/java/com/reactnativenavigation/parse/FractionParser.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/FractionParser.java View File

1
-package com.reactnativenavigation.parse;
1
+package com.reactnativenavigation.parse.parsers;
2
+
3
+import com.reactnativenavigation.parse.params.Fraction;
4
+import com.reactnativenavigation.parse.params.NullFraction;
2
 
5
 
3
 import org.json.JSONObject;
6
 import org.json.JSONObject;
4
 
7
 

+ 23
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/InterpolationParser.java View File

1
+package com.reactnativenavigation.parse.parsers;
2
+
3
+
4
+import com.reactnativenavigation.parse.params.Interpolation;
5
+
6
+import org.json.JSONObject;
7
+
8
+public class InterpolationParser {
9
+    public static Interpolation parse(JSONObject json, String intepolation) {
10
+        Interpolation result = Interpolation.DEFAULT;
11
+        if (json.has(intepolation)) {
12
+            switch (json.optString(intepolation)) {
13
+                case "decelerate":
14
+                    result = Interpolation.DECELERATING;
15
+                    break;
16
+                case "accelerate":
17
+                    result = Interpolation.ACCELERATING;
18
+                    break;
19
+            }
20
+        }
21
+        return result;
22
+    }
23
+}

lib/android/app/src/main/java/com/reactnativenavigation/parse/JSONParser.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/JSONParser.java View File

1
-package com.reactnativenavigation.parse;
1
+package com.reactnativenavigation.parse.parsers;
2
 
2
 
3
 import com.facebook.react.bridge.ReadableArray;
3
 import com.facebook.react.bridge.ReadableArray;
4
 import com.facebook.react.bridge.ReadableMap;
4
 import com.facebook.react.bridge.ReadableMap;

lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutNodeParser.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/LayoutNodeParser.java View File

1
-package com.reactnativenavigation.parse;
1
+package com.reactnativenavigation.parse.parsers;
2
 
2
 
3
 import android.support.annotation.NonNull;
3
 import android.support.annotation.NonNull;
4
 
4
 
5
+import com.reactnativenavigation.parse.LayoutNode;
6
+
5
 import org.json.JSONArray;
7
 import org.json.JSONArray;
6
 import org.json.JSONObject;
8
 import org.json.JSONObject;
7
 
9
 

lib/android/app/src/main/java/com/reactnativenavigation/parse/NumberParser.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/NumberParser.java View File

1
-package com.reactnativenavigation.parse;
1
+package com.reactnativenavigation.parse.parsers;
2
+
3
+import com.reactnativenavigation.parse.params.NullNumber;
4
+import com.reactnativenavigation.parse.params.Number;
2
 
5
 
3
 import org.json.JSONObject;
6
 import org.json.JSONObject;
4
 
7
 

lib/android/app/src/main/java/com/reactnativenavigation/parse/TextParser.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/parsers/TextParser.java View File

1
-package com.reactnativenavigation.parse;
1
+package com.reactnativenavigation.parse.parsers;
2
+
3
+import com.reactnativenavigation.parse.params.NullText;
4
+import com.reactnativenavigation.parse.params.Text;
2
 
5
 
3
 import org.json.JSONObject;
6
 import org.json.JSONObject;
4
 
7
 

+ 59
- 0
lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabsOptionsPresenter.java View File

1
+package com.reactnativenavigation.presentation;
2
+
3
+import com.reactnativenavigation.parse.BottomTabOptions;
4
+import com.reactnativenavigation.parse.BottomTabsOptions;
5
+import com.reactnativenavigation.parse.Options;
6
+import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabFinder;
7
+import com.reactnativenavigation.views.BottomTabs;
8
+
9
+public class BottomTabsOptionsPresenter {
10
+    private BottomTabs bottomTabs;
11
+    private BottomTabFinder bottomTabFinder;
12
+
13
+    public BottomTabsOptionsPresenter(BottomTabs bottomTabs, BottomTabFinder bottomTabFinder) {
14
+        this.bottomTabs = bottomTabs;
15
+        this.bottomTabFinder = bottomTabFinder;
16
+    }
17
+
18
+    public void present(Options options) {
19
+        applyBottomTabsOptions(options.bottomTabsOptions);
20
+    }
21
+
22
+    public void present(Options options, int tabIndex) {
23
+        applyBottomTabOptions(options.bottomTabOptions, tabIndex);
24
+    }
25
+
26
+    private void applyBottomTabOptions(BottomTabOptions options, int tabIndex) {
27
+        if (options.badge.hasValue()) {
28
+            bottomTabs.setBadge(tabIndex, options.badge);
29
+        }
30
+    }
31
+
32
+    private void applyBottomTabsOptions(BottomTabsOptions options) {
33
+        if (options.backgroundColor.hasValue()) {
34
+            bottomTabs.setBackgroundColor(options.backgroundColor.get());
35
+        }
36
+        if (options.currentTabIndex.hasValue()) {
37
+            bottomTabs.setCurrentItem(options.currentTabIndex.get());
38
+        }
39
+        if (options.testId.hasValue()) {
40
+            bottomTabs.setTag(options.testId.get());
41
+        }
42
+        if (options.selectedTabColor.hasValue()) {
43
+            bottomTabs.setAccentColor(options.selectedTabColor.get());
44
+        }
45
+        if (options.tabColor.hasValue()) {
46
+            bottomTabs.setInactiveColor(options.tabColor.get());
47
+        }
48
+        if (options.currentTabId.hasValue()) {
49
+            int tabIndex = bottomTabFinder.findByControllerId(options.currentTabId.get());
50
+            if (tabIndex >= 0) bottomTabs.setCurrentItem(tabIndex);
51
+        }
52
+        if (options.visible.isTrueOrUndefined()) {
53
+            bottomTabs.restoreBottomNavigation(options.animate.isTrueOrUndefined());
54
+        }
55
+        if (options.visible.isFalse()) {
56
+            bottomTabs.hideBottomNavigation(options.animate.isTrueOrUndefined());
57
+        }
58
+    }
59
+}

+ 231
- 0
lib/android/app/src/main/java/com/reactnativenavigation/presentation/FabOptionsPresenter.java View File

1
+package com.reactnativenavigation.presentation;
2
+
3
+
4
+import android.support.annotation.NonNull;
5
+import android.view.Gravity;
6
+import android.view.View;
7
+import android.view.ViewGroup;
8
+import android.widget.FrameLayout;
9
+import android.widget.RelativeLayout;
10
+
11
+import com.reactnativenavigation.R;
12
+import com.reactnativenavigation.parse.FabOptions;
13
+import com.reactnativenavigation.parse.Options;
14
+import com.reactnativenavigation.views.Fab;
15
+import com.reactnativenavigation.views.FabMenu;
16
+import com.reactnativenavigation.views.ReactComponent;
17
+
18
+import static android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM;
19
+import static android.widget.RelativeLayout.ALIGN_PARENT_LEFT;
20
+import static android.widget.RelativeLayout.ALIGN_PARENT_RIGHT;
21
+import static android.widget.RelativeLayout.ALIGN_PARENT_TOP;
22
+import static com.github.clans.fab.FloatingActionButton.SIZE_MINI;
23
+import static com.github.clans.fab.FloatingActionButton.SIZE_NORMAL;
24
+
25
+public class FabOptionsPresenter {
26
+    private ViewGroup viewGroup;
27
+    private ReactComponent component;
28
+
29
+    private Fab fab;
30
+    private FabMenu fabMenu;
31
+
32
+    public void applyOptions(FabOptions options, @NonNull ReactComponent component, @NonNull ViewGroup viewGroup) {
33
+        this.viewGroup = viewGroup;
34
+        this.component = component;
35
+
36
+        if (options.id.hasValue()) {
37
+            if (fabMenu != null && fabMenu.getFabId().equals(options.id.get())) {
38
+                setParams(fabMenu, options);
39
+                fabMenu.bringToFront();
40
+                applyFabMenuOptions(fabMenu, options);
41
+            } else if (fab != null && fab.getFabId().equals(options.id.get())) {
42
+                setParams(fab, options);
43
+                fab.bringToFront();
44
+                applyFabOptions(fab, options);
45
+                fab.setOnClickListener(v -> component.sendOnNavigationButtonPressed(options.id.get()));
46
+            } else {
47
+                createFab(options);
48
+            }
49
+        } else {
50
+            removeFab();
51
+            removeFabMenu();
52
+        }
53
+    }
54
+
55
+    private void createFab(FabOptions options) {
56
+        if (options.actionsArray.size() > 0) {
57
+            fabMenu = new FabMenu(viewGroup.getContext(), options.id.get());
58
+            setParams(fabMenu, options);
59
+            applyFabMenuOptions(fabMenu, options);
60
+            viewGroup.addView(fabMenu);
61
+        } else {
62
+            fab = new Fab(viewGroup.getContext(), options.id.get());
63
+            setParams(fab, options);
64
+            applyFabOptions(fab, options);
65
+            fab.setOnClickListener(v -> component.sendOnNavigationButtonPressed(options.id.get()));
66
+            viewGroup.addView(fab);
67
+        }
68
+    }
69
+
70
+    private void removeFabMenu() {
71
+        if (fabMenu != null) {
72
+            fabMenu.hideMenuButton(true);
73
+            viewGroup.removeView(fabMenu);
74
+            fabMenu = null;
75
+        }
76
+    }
77
+
78
+    private void removeFab() {
79
+        if (fab != null) {
80
+            fab.hide(true);
81
+            viewGroup.removeView(fab);
82
+            fab = null;
83
+        }
84
+    }
85
+
86
+    private void setParams(View fab, FabOptions options) {
87
+        ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
88
+        if (viewGroup instanceof RelativeLayout) {
89
+            RelativeLayout.LayoutParams layoutParamsRelative = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
90
+            layoutParamsRelative.bottomMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
91
+            layoutParamsRelative.rightMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
92
+            layoutParamsRelative.leftMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
93
+            layoutParamsRelative.topMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
94
+            if (options.alignHorizontally.hasValue()) {
95
+                if ("right".equals(options.alignHorizontally.get())) {
96
+                    layoutParamsRelative.removeRule(ALIGN_PARENT_LEFT);
97
+                    layoutParamsRelative.addRule(ALIGN_PARENT_RIGHT);
98
+                }
99
+                if ("left".equals(options.alignHorizontally.get())) {
100
+                    layoutParamsRelative.removeRule(ALIGN_PARENT_RIGHT);
101
+                    layoutParamsRelative.addRule(ALIGN_PARENT_LEFT);
102
+                }
103
+            } else {
104
+                layoutParamsRelative.addRule(ALIGN_PARENT_RIGHT);
105
+            }
106
+            if (options.alignVertically.hasValue()) {
107
+                if ("top".equals(options.alignVertically.get())) {
108
+                    layoutParamsRelative.removeRule(ALIGN_PARENT_BOTTOM);
109
+                    layoutParamsRelative.addRule(ALIGN_PARENT_TOP);
110
+                }
111
+                if ("bottom".equals(options.alignVertically.get())) {
112
+                    layoutParamsRelative.removeRule(ALIGN_PARENT_TOP);
113
+                    layoutParamsRelative.addRule(ALIGN_PARENT_BOTTOM);
114
+                }
115
+            } else {
116
+                layoutParamsRelative.addRule(ALIGN_PARENT_BOTTOM);
117
+            }
118
+            layoutParams = layoutParamsRelative;
119
+        }
120
+        if (viewGroup instanceof FrameLayout) {
121
+            FrameLayout.LayoutParams layoutParamsFrame = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
122
+            layoutParamsFrame.bottomMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
123
+            layoutParamsFrame.rightMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
124
+            layoutParamsFrame.leftMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
125
+            layoutParamsFrame.topMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
126
+            if (options.alignHorizontally.hasValue()) {
127
+                if ("right".equals(options.alignHorizontally.get())) {
128
+                    removeGravityParam(layoutParamsFrame, Gravity.LEFT);
129
+                    setGravityParam(layoutParamsFrame, Gravity.RIGHT);
130
+                }
131
+                if ("left".equals(options.alignHorizontally.get())) {
132
+                    removeGravityParam(layoutParamsFrame, Gravity.RIGHT);
133
+                    setGravityParam(layoutParamsFrame, Gravity.LEFT);
134
+                }
135
+            } else {
136
+                setGravityParam(layoutParamsFrame, Gravity.RIGHT);
137
+            }
138
+            if (options.alignVertically.hasValue()) {
139
+                if ("top".equals(options.alignVertically.get())) {
140
+                    removeGravityParam(layoutParamsFrame, Gravity.BOTTOM);
141
+                    setGravityParam(layoutParamsFrame, Gravity.TOP);
142
+                }
143
+                if ("bottom".equals(options.alignVertically.get())) {
144
+                    removeGravityParam(layoutParamsFrame, Gravity.TOP);
145
+                    setGravityParam(layoutParamsFrame, Gravity.BOTTOM);
146
+                }
147
+            } else {
148
+                setGravityParam(layoutParamsFrame, Gravity.BOTTOM);
149
+            }
150
+            layoutParams = layoutParamsFrame;
151
+        }
152
+        fab.setLayoutParams(layoutParams);
153
+    }
154
+
155
+    private void applyFabOptions(Fab fab, FabOptions options) {
156
+        if (options.visible.isTrueOrUndefined()) {
157
+            fab.show(true);
158
+        }
159
+        if (options.visible.isFalse()) {
160
+            fab.hide(true);
161
+        }
162
+        if (options.backgroundColor.hasValue()) {
163
+            fab.setColorNormal(options.backgroundColor.get());
164
+        }
165
+        if (options.clickColor.hasValue()) {
166
+            fab.setColorPressed(options.clickColor.get());
167
+        }
168
+        if (options.rippleColor.hasValue()) {
169
+            fab.setColorRipple(options.rippleColor.get());
170
+        }
171
+        if (options.icon.hasValue()) {
172
+            fab.applyIcon(options.icon.get());
173
+        }
174
+        if (options.size.hasValue()) {
175
+            fab.setButtonSize("mini".equals(options.size.get()) ? SIZE_MINI : SIZE_NORMAL);
176
+        }
177
+        if (options.hideOnScroll.isTrue()) {
178
+            fab.enableCollapse(component.getScrollEventListener());
179
+        }
180
+        if (options.hideOnScroll.isFalseOrUndefined()) {
181
+            fab.disableCollapse();
182
+        }
183
+    }
184
+
185
+    private void applyFabMenuOptions(FabMenu fabMenu, FabOptions options) {
186
+        if (options.visible.isTrueOrUndefined()) {
187
+            fabMenu.showMenuButton(true);
188
+        }
189
+        if (options.visible.isFalse()) {
190
+            fabMenu.hideMenuButton(true);
191
+        }
192
+
193
+        if (options.backgroundColor.hasValue()) {
194
+            fabMenu.setMenuButtonColorNormal(options.backgroundColor.get());
195
+        }
196
+        if (options.clickColor.hasValue()) {
197
+            fabMenu.setMenuButtonColorPressed(options.clickColor.get());
198
+        }
199
+        if (options.rippleColor.hasValue()) {
200
+            fabMenu.setMenuButtonColorRipple(options.rippleColor.get());
201
+        }
202
+        for (Fab fabStored : fabMenu.getActions()) {
203
+            fabMenu.removeMenuButton(fabStored);
204
+        }
205
+        fabMenu.getActions().clear();
206
+        for (FabOptions fabOption : options.actionsArray) {
207
+            Fab fab = new Fab(viewGroup.getContext(), fabOption.id.get());
208
+            applyFabOptions(fab, fabOption);
209
+            fab.setOnClickListener(v -> component.sendOnNavigationButtonPressed(options.id.get()));
210
+
211
+            fabMenu.getActions().add(fab);
212
+            fabMenu.addMenuButton(fab);
213
+        }
214
+        if (options.hideOnScroll.isTrue()) {
215
+            fabMenu.enableCollapse(component.getScrollEventListener());
216
+        }
217
+        if (options.hideOnScroll.isFalseOrUndefined()) {
218
+            fabMenu.disableCollapse();
219
+        }
220
+    }
221
+
222
+    private void setGravityParam(FrameLayout.LayoutParams params, int gravityParam) {
223
+        params.gravity = params.gravity | gravityParam;
224
+    }
225
+
226
+    private void removeGravityParam(FrameLayout.LayoutParams params, int gravityParam) {
227
+        if ((params.gravity & gravityParam) == gravityParam) {
228
+            params.gravity = params.gravity & ~gravityParam;
229
+        }
230
+    }
231
+}

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

1
 package com.reactnativenavigation.presentation;
1
 package com.reactnativenavigation.presentation;
2
 
2
 
3
-import com.reactnativenavigation.parse.Button;
3
+import com.reactnativenavigation.parse.params.Button;
4
 import com.reactnativenavigation.parse.Options;
4
 import com.reactnativenavigation.parse.Options;
5
 import com.reactnativenavigation.parse.TopBarOptions;
5
 import com.reactnativenavigation.parse.TopBarOptions;
6
 import com.reactnativenavigation.parse.TopTabOptions;
6
 import com.reactnativenavigation.parse.TopTabOptions;
10
 
10
 
11
 import java.util.ArrayList;
11
 import java.util.ArrayList;
12
 
12
 
13
-import static com.reactnativenavigation.parse.Options.BooleanOptions.False;
14
-import static com.reactnativenavigation.parse.Options.BooleanOptions.True;
15
-
16
 public class OptionsPresenter {
13
 public class OptionsPresenter {
17
     private TopBar topBar;
14
     private TopBar topBar;
18
     private ReactComponent component;
15
     private ReactComponent component;
22
         this.component = component;
19
         this.component = component;
23
     }
20
     }
24
 
21
 
22
+    public OptionsPresenter(TopBar topBar) {
23
+        this.topBar = topBar;
24
+    }
25
+
25
     public void applyOptions(Options options) {
26
     public void applyOptions(Options options) {
26
         applyButtons(options.topBarOptions.leftButtons, options.topBarOptions.rightButtons);
27
         applyButtons(options.topBarOptions.leftButtons, options.topBarOptions.rightButtons);
27
         applyTopBarOptions(options.topBarOptions);
28
         applyTopBarOptions(options.topBarOptions);
34
         topBar.setBackgroundColor(options.backgroundColor);
35
         topBar.setBackgroundColor(options.backgroundColor);
35
         topBar.setTitleTextColor(options.textColor);
36
         topBar.setTitleTextColor(options.textColor);
36
         topBar.setTitleFontSize(options.textFontSize);
37
         topBar.setTitleFontSize(options.textFontSize);
38
+        if (options.testId.hasValue()) topBar.setTestId(options.testId.get());
37
 
39
 
38
         topBar.setTitleTypeface(options.textFontFamily);
40
         topBar.setTitleTypeface(options.textFontFamily);
39
-        if (options.hidden == True) {
40
-            topBar.hide(options.animateHide);
41
+        if (options.visible.isFalse()) {
42
+            topBar.hide(options.animate);
41
         }
43
         }
42
-        if (options.hidden == False) {
43
-            topBar.show(options.animateHide);
44
+        if (options.visible.isTrueOrUndefined()) {
45
+            topBar.show(options.animate);
44
         }
46
         }
45
-        if (options.drawBehind == True) {
47
+        if (options.drawBehind.isTrue()) {
46
             component.drawBehindTopBar();
48
             component.drawBehindTopBar();
47
-        } else if (options.drawBehind == False) {
49
+        } else if (options.drawBehind.isFalseOrUndefined()) {
48
             component.drawBelowTopBar(topBar);
50
             component.drawBelowTopBar(topBar);
49
         }
51
         }
50
 
52
 
51
-        if (options.hideOnScroll == True) {
53
+        if (options.hideOnScroll.isTrue()) {
52
             topBar.enableCollapse(component.getScrollEventListener());
54
             topBar.enableCollapse(component.getScrollEventListener());
53
-        } else if (options.hideOnScroll == False) {
55
+        } else if (options.hideOnScroll.isTrue()) {
54
             topBar.disableCollapse();
56
             topBar.disableCollapse();
55
         }
57
         }
56
     }
58
     }
62
     private void applyTopTabsOptions(TopTabsOptions options) {
64
     private void applyTopTabsOptions(TopTabsOptions options) {
63
         topBar.applyTopTabsColors(options.selectedTabColor, options.unselectedTabColor);
65
         topBar.applyTopTabsColors(options.selectedTabColor, options.unselectedTabColor);
64
         topBar.applyTopTabsFontSize(options.fontSize);
66
         topBar.applyTopTabsFontSize(options.fontSize);
67
+        topBar.setTopTabsVisible(options.visible.isTrueOrUndefined());
65
     }
68
     }
66
 
69
 
67
     private void applyTopTabOptions(TopTabOptions topTabOptions) {
70
     private void applyTopTabOptions(TopTabOptions topTabOptions) {
69
             topBar.setTopTabFontFamily(topTabOptions.tabIndex, topTabOptions.fontFamily);
72
             topBar.setTopTabFontFamily(topTabOptions.tabIndex, topTabOptions.fontFamily);
70
         }
73
         }
71
     }
74
     }
75
+
76
+    public void onChildWillDisappear(Options disappearing, Options appearing) {
77
+        if (disappearing.topBarOptions.visible.isTrueOrUndefined() && appearing.topBarOptions.visible.isFalse()) {
78
+            topBar.hide(disappearing.topBarOptions.animate);
79
+        }
80
+    }
72
 }
81
 }

+ 24
- 0
lib/android/app/src/main/java/com/reactnativenavigation/presentation/SideMenuOptionsPresenter.java View File

1
+package com.reactnativenavigation.presentation;
2
+
3
+import android.support.v4.widget.DrawerLayout;
4
+import android.view.Gravity;
5
+
6
+import com.reactnativenavigation.parse.SideMenuRootOptions;
7
+
8
+public class SideMenuOptionsPresenter {
9
+
10
+    private DrawerLayout sideMenu;
11
+
12
+    public SideMenuOptionsPresenter(DrawerLayout sideMenu) {
13
+        this.sideMenu = sideMenu;
14
+    }
15
+
16
+    public void present(SideMenuRootOptions options) {
17
+        if (options.left.visible.isTrue()) {
18
+            sideMenu.openDrawer(Gravity.LEFT);
19
+        }
20
+        if (options.right.visible.isTrue()) {
21
+            sideMenu.openDrawer(Gravity.RIGHT);
22
+        }
23
+    }
24
+}

+ 17
- 7
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationEvent.java View File

1
 package com.reactnativenavigation.react;
1
 package com.reactnativenavigation.react;
2
 
2
 
3
+import android.support.annotation.NonNull;
4
+
3
 import com.facebook.react.bridge.Arguments;
5
 import com.facebook.react.bridge.Arguments;
4
 import com.facebook.react.bridge.ReactContext;
6
 import com.facebook.react.bridge.ReactContext;
5
 import com.facebook.react.bridge.WritableMap;
7
 import com.facebook.react.bridge.WritableMap;
6
-import com.facebook.react.bridge.WritableNativeArray;
7
 import com.facebook.react.modules.core.DeviceEventManagerModule;
8
 import com.facebook.react.modules.core.DeviceEventManagerModule;
8
 
9
 
9
-import org.json.JSONException;
10
-import org.json.JSONObject;
11
-
12
 import static com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
10
 import static com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
13
 
11
 
14
 public class NavigationEvent {
12
 public class NavigationEvent {
16
 	private static final String componentDidAppear = "RNN.componentDidAppear";
14
 	private static final String componentDidAppear = "RNN.componentDidAppear";
17
 	private static final String componentDidDisappear = "RNN.componentDidDisappear";
15
 	private static final String componentDidDisappear = "RNN.componentDidDisappear";
18
 	private static final String onNavigationButtonPressed = "RNN.navigationButtonPressed";
16
 	private static final String onNavigationButtonPressed = "RNN.navigationButtonPressed";
17
+    private static final String componentLifecycle = "RNN.componentLifecycle";
19
 
18
 
20
 	private final RCTDeviceEventEmitter emitter;
19
 	private final RCTDeviceEventEmitter emitter;
21
 
20
 
27
 		emit(onAppLaunched);
26
 		emit(onAppLaunched);
28
 	}
27
 	}
29
 
28
 
30
-	public void componentDidDisappear(String id) {
29
+	public void componentDidDisappear(String id, String componentName) {
31
 		emit(componentDidDisappear, id);
30
 		emit(componentDidDisappear, id);
31
+		emit(componentLifecycle, getLifecycleEventData(id, componentName, "didDisappear"));
32
 	}
32
 	}
33
 
33
 
34
-	public void componentDidAppear(String id) {
34
+	public void componentDidAppear(String id, String componentName) {
35
 		emit(componentDidAppear, id);
35
 		emit(componentDidAppear, id);
36
+        emit(componentLifecycle, getLifecycleEventData(id, componentName, "didAppear"));
36
 	}
37
 	}
37
 
38
 
38
-	public void sendOnNavigationButtonPressed(String id, String buttonId) {
39
+    @NonNull
40
+    private WritableMap getLifecycleEventData(String id, String componentName, String didAppear) {
41
+        WritableMap map = Arguments.createMap();
42
+        map.putString("componentId", id);
43
+        map.putString("componentName", componentName);
44
+        map.putString("event", didAppear);
45
+        return map;
46
+    }
47
+
48
+    public void sendOnNavigationButtonPressed(String id, String buttonId) {
39
 		WritableMap map = Arguments.createMap();
49
 		WritableMap map = Arguments.createMap();
40
 		map.putString("componentId", id);
50
 		map.putString("componentId", id);
41
 		map.putString("buttonId", buttonId);
51
 		map.putString("buttonId", buttonId);

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

9
 import com.facebook.react.bridge.ReactMethod;
9
 import com.facebook.react.bridge.ReactMethod;
10
 import com.facebook.react.bridge.ReadableMap;
10
 import com.facebook.react.bridge.ReadableMap;
11
 import com.reactnativenavigation.NavigationActivity;
11
 import com.reactnativenavigation.NavigationActivity;
12
-import com.reactnativenavigation.parse.JSONParser;
12
+import com.reactnativenavigation.parse.parsers.JSONParser;
13
 import com.reactnativenavigation.parse.LayoutFactory;
13
 import com.reactnativenavigation.parse.LayoutFactory;
14
 import com.reactnativenavigation.parse.LayoutNode;
14
 import com.reactnativenavigation.parse.LayoutNode;
15
-import com.reactnativenavigation.parse.LayoutNodeParser;
15
+import com.reactnativenavigation.parse.parsers.LayoutNodeParser;
16
 import com.reactnativenavigation.parse.Options;
16
 import com.reactnativenavigation.parse.Options;
17
 import com.reactnativenavigation.utils.TypefaceLoader;
17
 import com.reactnativenavigation.utils.TypefaceLoader;
18
 import com.reactnativenavigation.utils.UiThread;
18
 import com.reactnativenavigation.utils.UiThread;

+ 2
- 2
lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java View File

60
 
60
 
61
 	@Override
61
 	@Override
62
 	public void sendComponentStart() {
62
 	public void sendComponentStart() {
63
-		new NavigationEvent(reactInstanceManager.getCurrentReactContext()).componentDidAppear(componentId);
63
+		new NavigationEvent(reactInstanceManager.getCurrentReactContext()).componentDidAppear(componentId, componentName);
64
 	}
64
 	}
65
 
65
 
66
 	@Override
66
 	@Override
67
 	public void sendComponentStop() {
67
 	public void sendComponentStop() {
68
-		new NavigationEvent(reactInstanceManager.getCurrentReactContext()).componentDidDisappear(componentId);
68
+		new NavigationEvent(reactInstanceManager.getCurrentReactContext()).componentDidDisappear(componentId, componentName);
69
 	}
69
 	}
70
 
70
 
71
     @Override
71
     @Override

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

1
+package com.reactnativenavigation.utils;
2
+
3
+public class ArrayUtils {
4
+    public static boolean contains(Object[] array, Object item) {
5
+        if (isNullOrEmpty(array)) return false;
6
+        for (Object o : array) {
7
+            if (o == item) return true;
8
+        }
9
+        return false;
10
+    }
11
+
12
+    private static boolean isNullOrEmpty(Object[] array) {
13
+        return array == null || array.length == 0;
14
+    }
15
+}

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

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

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

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

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

1
 package com.reactnativenavigation.utils;
1
 package com.reactnativenavigation.utils;
2
 
2
 
3
 import android.content.Context;
3
 import android.content.Context;
4
+import android.content.res.Resources;
4
 import android.graphics.PorterDuff;
5
 import android.graphics.PorterDuff;
5
 import android.graphics.PorterDuffColorFilter;
6
 import android.graphics.PorterDuffColorFilter;
6
 import android.graphics.drawable.Drawable;
7
 import android.graphics.drawable.Drawable;
8
+import android.os.Build;
7
 import android.os.Handler;
9
 import android.os.Handler;
8
 import android.os.Looper;
10
 import android.os.Looper;
9
 import android.util.DisplayMetrics;
11
 import android.util.DisplayMetrics;
11
 import android.view.ViewTreeObserver;
13
 import android.view.ViewTreeObserver;
12
 import android.view.WindowManager;
14
 import android.view.WindowManager;
13
 
15
 
16
+import com.reactnativenavigation.NavigationApplication;
17
+
14
 public class UiUtils {
18
 public class UiUtils {
19
+    public static final int STATUS_BAR_HEIGHT_M = 24;
20
+    public static final int STATUS_BAR_HEIGHT_L = 25;
21
+    private static int statusBarHeight = -1;
22
+
15
 	public static void runOnPreDrawOnce(final View view, final Runnable task) {
23
 	public static void runOnPreDrawOnce(final View view, final Runnable task) {
16
         view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
24
         view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
17
             @Override
25
             @Override
39
 		}
47
 		}
40
 		return metrics.heightPixels;
48
 		return metrics.heightPixels;
41
 	}
49
 	}
50
+
51
+    public static int getStatusBarHeight(Context context) {
52
+        if (statusBarHeight > 0) {
53
+            return statusBarHeight;
54
+        }
55
+        final Resources resources = context.getResources();
56
+        final int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
57
+        statusBarHeight = resourceId > 0 ?
58
+                resources.getDimensionPixelSize(resourceId) :
59
+                (int) dpToPx(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? STATUS_BAR_HEIGHT_M : STATUS_BAR_HEIGHT_L);
60
+        return statusBarHeight;
61
+    }
62
+
63
+    public static float dpToPx(float dp) {
64
+        float scale = NavigationApplication.instance.getResources().getDisplayMetrics().density;
65
+        return dp * scale + 0.5f;
66
+    }
67
+
42
 }
68
 }

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

4
 import android.view.View;
4
 import android.view.View;
5
 import android.view.ViewGroup;
5
 import android.view.ViewGroup;
6
 
6
 
7
+import java.util.ArrayList;
8
+import java.util.List;
9
+
7
 public class ViewUtils {
10
 public class ViewUtils {
8
     @Nullable
11
     @Nullable
9
     public static <T> T findChildByClass(ViewGroup root, Class clazz) {
12
     public static <T> T findChildByClass(ViewGroup root, Class clazz) {
22
         }
25
         }
23
         return null;
26
         return null;
24
     }
27
     }
28
+
29
+    public static <T> List<T> findChildrenByClassRecursive(ViewGroup root, Class clazz) {
30
+        ArrayList<T> ret = new ArrayList<>();
31
+        for (int i = 0; i < root.getChildCount(); i++) {
32
+            View view = root.getChildAt(i);
33
+            if (view instanceof ViewGroup) {
34
+                ret.addAll(findChildrenByClassRecursive((ViewGroup) view, clazz));
35
+            }
36
+            if (clazz.isAssignableFrom(view.getClass())) {
37
+                ret.add((T) view);
38
+            }
39
+        }
40
+        return ret;
41
+    }
42
+
43
+    public static <T> List<T> findChildrenByClass(ViewGroup root, Class clazz) {
44
+        List<T> ret = new ArrayList<>();
45
+        for (int i = 0; i < root.getChildCount(); i++) {
46
+            View view = root.getChildAt(i);
47
+            if (clazz.isAssignableFrom(view.getClass())) {
48
+                ret.add((T) view);
49
+            }
50
+        }
51
+        return ret;
52
+    }
25
 }
53
 }

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

1
-package com.reactnativenavigation.viewcontrollers;
2
-
3
-import android.app.Activity;
4
-import android.graphics.Color;
5
-import android.support.annotation.NonNull;
6
-import android.support.design.widget.BottomNavigationView;
7
-import android.view.Menu;
8
-import android.view.MenuItem;
9
-import android.view.View;
10
-import android.view.ViewGroup;
11
-import android.widget.RelativeLayout;
12
-
13
-import com.reactnativenavigation.parse.Options;
14
-import com.reactnativenavigation.parse.Text;
15
-import com.reactnativenavigation.presentation.NavigationOptionsListener;
16
-import com.reactnativenavigation.utils.CompatUtils;
17
-
18
-import java.util.ArrayList;
19
-import java.util.Collection;
20
-import java.util.List;
21
-
22
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
23
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
24
-import static android.widget.RelativeLayout.ABOVE;
25
-import static android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM;
26
-import static com.reactnativenavigation.parse.DEFAULT_VALUES.NO_INT_VALUE;
27
-
28
-public class BottomTabsController extends ParentController
29
-		implements BottomNavigationView.OnNavigationItemSelectedListener, NavigationOptionsListener {
30
-	private BottomNavigationView bottomNavigationView;
31
-	private List<ViewController> tabs = new ArrayList<>();
32
-	private int selectedIndex = 0;
33
-
34
-	public BottomTabsController(final Activity activity, final String id) {
35
-		super(activity, id);
36
-	}
37
-
38
-	@NonNull
39
-	@Override
40
-	protected ViewGroup createView() {
41
-		RelativeLayout root = new RelativeLayout(getActivity());
42
-		bottomNavigationView = new BottomNavigationView(getActivity());
43
-		bottomNavigationView.setId(CompatUtils.generateViewId());
44
-		bottomNavigationView.setBackgroundColor(Color.DKGRAY);
45
-		bottomNavigationView.setOnNavigationItemSelectedListener(this);
46
-		RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
47
-		lp.addRule(ALIGN_PARENT_BOTTOM);
48
-		root.addView(bottomNavigationView, lp);
49
-		return root;
50
-	}
51
-
52
-	@Override
53
-	public boolean handleBack() {
54
-		return !tabs.isEmpty() && tabs.get(selectedIndex).handleBack();
55
-	}
56
-
57
-	@Override
58
-	public boolean onNavigationItemSelected(@NonNull final MenuItem item) {
59
-		selectTabAtIndex(item.getItemId());
60
-		return true;
61
-	}
62
-
63
-	void selectTabAtIndex(final int newIndex) {
64
-		tabs.get(selectedIndex).getView().setVisibility(View.GONE);
65
-		selectedIndex = newIndex;
66
-		tabs.get(selectedIndex).getView().setVisibility(View.VISIBLE);
67
-	}
68
-
69
-	public void setTabs(final List<ViewController> tabs) {
70
-		if (tabs.size() > 5) {
71
-			throw new RuntimeException("Too many tabs!");
72
-		}
73
-		this.tabs = tabs;
74
-		getView();
75
-		for (int i = 0; i < tabs.size(); i++) {
76
-			String title = String.valueOf(i);
77
-			createTab(tabs.get(i), i, title);
78
-		}
79
-		selectTabAtIndex(0);
80
-	}
81
-
82
-	private void createTab(ViewController tab, final int index, final String title) {
83
-		bottomNavigationView.getMenu().add(0, index, Menu.NONE, title);
84
-		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
85
-		params.addRule(ABOVE, bottomNavigationView.getId());
86
-		tab.getView().setVisibility(View.GONE);
87
-		getView().addView(tab.getView(), params);
88
-	}
89
-
90
-	int getSelectedIndex() {
91
-		return selectedIndex;
92
-	}
93
-
94
-	@NonNull
95
-	@Override
96
-	public Collection<ViewController> getChildControllers() {
97
-		return tabs;
98
-	}
99
-
100
-	@Override
101
-	public void mergeOptions(Options options) {
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
-    }
119
-
120
-	private boolean hasControlWithId(StackController controller, String id) {
121
-		for (ViewController child : controller.getChildControllers()) {
122
-			if (id.equals(child.getId())) {
123
-				return true;
124
-			}
125
-			if (child instanceof StackController) {
126
-				return hasControlWithId((StackController) child, id);
127
-			}
128
-		}
129
-		return false;
130
-	}
131
-}

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

18
                                    final String id,
18
                                    final String id,
19
                                    final String componentName,
19
                                    final String componentName,
20
                                    final ReactViewCreator viewCreator,
20
                                    final ReactViewCreator viewCreator,
21
-                                   final Options initialNavigationOptions) {
22
-        super(activity, id);
21
+                                   final Options initialOptions) {
22
+        super(activity, id, initialOptions);
23
         this.componentName = componentName;
23
         this.componentName = componentName;
24
         this.viewCreator = viewCreator;
24
         this.viewCreator = viewCreator;
25
-        options = initialNavigationOptions;
26
     }
25
     }
27
 
26
 
28
     @Override
27
     @Override
29
     public void onViewAppeared() {
28
     public void onViewAppeared() {
30
         super.onViewAppeared();
29
         super.onViewAppeared();
31
-        view.applyOptions(options);
32
         view.sendComponentStart();
30
         view.sendComponentStart();
33
     }
31
     }
34
 
32
 
38
         super.onViewDisappear();
36
         super.onViewDisappear();
39
     }
37
     }
40
 
38
 
39
+    @Override
40
+    public void sendOnNavigationButtonPressed(String buttonId) {
41
+        getView().sendOnNavigationButtonPressed(buttonId);
42
+    }
43
+
44
+    @Override
45
+    public void applyOptions(Options options) {
46
+        view.applyOptions(options);
47
+    }
48
+
41
     @Override
49
     @Override
42
     protected boolean isViewShown() {
50
     protected boolean isViewShown() {
43
         return super.isViewShown() && view.isReady();
51
         return super.isViewShown() && view.isReady();
52
 
60
 
53
     @Override
61
     @Override
54
     public void mergeOptions(Options options) {
62
     public void mergeOptions(Options options) {
55
-        this.options.mergeWith(options);
63
+        this.options = this.options.mergeWith(options);
56
         view.applyOptions(this.options);
64
         view.applyOptions(this.options);
57
         applyOnParentController(parentController -> parentController.applyOptions(this.options, view));
65
         applyOnParentController(parentController -> parentController.applyOptions(this.options, view));
58
     }
66
     }
59
 
67
 
60
-    Options getOptions() {
61
-        return options;
62
-    }
63
-
64
     ReactComponent getComponent() {
68
     ReactComponent getComponent() {
65
         return view;
69
         return view;
66
     }
70
     }

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

1
 package com.reactnativenavigation.viewcontrollers;
1
 package com.reactnativenavigation.viewcontrollers;
2
 
2
 
3
-import android.app.Dialog;
4
-import android.content.DialogInterface;
5
 import android.support.annotation.Nullable;
3
 import android.support.annotation.Nullable;
6
-import android.view.KeyEvent;
7
-import android.view.View;
8
 
4
 
9
 import com.facebook.react.bridge.Promise;
5
 import com.facebook.react.bridge.Promise;
10
-import com.reactnativenavigation.R;
6
+import com.reactnativenavigation.viewcontrollers.modal.Modal;
7
+import com.reactnativenavigation.viewcontrollers.modal.ModalCreator;
11
 
8
 
12
 import java.util.ArrayList;
9
 import java.util.ArrayList;
13
 import java.util.List;
10
 import java.util.List;
14
 
11
 
15
-import static android.view.View.MeasureSpec.EXACTLY;
16
-import static android.view.View.MeasureSpec.makeMeasureSpec;
17
-
18
 public class ModalStack {
12
 public class ModalStack {
19
 
13
 
20
 	private List<Modal> modals = new ArrayList<>();
14
 	private List<Modal> modals = new ArrayList<>();
15
+    private ModalCreator creator;
16
+
17
+    public ModalStack(ModalCreator creator) {
18
+        this.creator = creator;
19
+    }
21
 
20
 
22
-	public void showModal(final ViewController viewController, Promise promise) {
23
-		Modal modal = new Modal(viewController);
24
-		modals.add(modal);
21
+    public void showModal(final ViewController viewController, Promise promise) {
22
+        Modal modal = creator.create(viewController);
23
+        modals.add(modal);
25
 		modal.show();
24
 		modal.show();
26
 		if (promise != null) {
25
 		if (promise != null) {
27
 			promise.resolve(viewController.getId());
26
 			promise.resolve(viewController.getId());
52
 	}
51
 	}
53
 
52
 
54
 	@Nullable
53
 	@Nullable
55
-	private Modal findModalByComponentId(String componentId) {
54
+	public Modal findModalByComponentId(String componentId) {
56
 		for (Modal modal : modals) {
55
 		for (Modal modal : modals) {
57
 			if (modal.containsDeepComponentId(componentId)) {
56
 			if (modal.containsDeepComponentId(componentId)) {
58
 				return modal;
57
 				return modal;
64
 	@Nullable
63
 	@Nullable
65
     ViewController findControllerById(String id) {
64
     ViewController findControllerById(String id) {
66
         Modal modal = findModalByComponentId(id);
65
         Modal modal = findModalByComponentId(id);
67
-        return modal != null ? modal.viewController : null;
68
-    }
69
-
70
-    private static class Modal implements DialogInterface.OnKeyListener {
71
-		public final ViewController viewController;
72
-		private final Dialog dialog;
73
-
74
-		Modal(final ViewController viewController) {
75
-			this.viewController = viewController;
76
-			dialog = new Dialog(viewController.getActivity(), R.style.Modal);
77
-			dialog.setOnKeyListener(this);
78
-		}
79
-
80
-		void show() {
81
-			preMeasureView();
82
-			dialog.setContentView(viewController.getView());
83
-			dialog.show();
84
-		}
85
-
86
-		void dismiss() {
87
-			dialog.dismiss();
88
-		}
89
-
90
-		boolean containsDeepComponentId(String componentId) {
91
-			return viewController.findControllerById(componentId) != null;
92
-		}
93
-
94
-		private void preMeasureView() {
95
-			View decorView = viewController.getActivity().getWindow().getDecorView();
96
-			viewController.getView().measure(makeMeasureSpec(decorView.getMeasuredWidth(), EXACTLY), makeMeasureSpec(decorView.getMeasuredHeight(), EXACTLY));
97
-		}
98
-
99
-        @Override
100
-        public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
101
-            if (keyCode == KeyEvent.KEYCODE_BACK) {
102
-                if (event.getAction() == KeyEvent.ACTION_UP) {
103
-                    if (viewController.handleBack()) {
104
-                        return true;
105
-                    }
106
-                    dialog.dismiss();
107
-                }
108
-            }
109
-            return false;
110
-        }
66
+        return modal != null ? modal.viewController.findControllerById(id) : null;
111
     }
67
     }
112
 }
68
 }

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

12
 import com.reactnativenavigation.presentation.OverlayManager;
12
 import com.reactnativenavigation.presentation.OverlayManager;
13
 import com.reactnativenavigation.utils.CompatUtils;
13
 import com.reactnativenavigation.utils.CompatUtils;
14
 import com.reactnativenavigation.utils.NoOpPromise;
14
 import com.reactnativenavigation.utils.NoOpPromise;
15
+import com.reactnativenavigation.viewcontrollers.modal.ModalCreator;
15
 
16
 
16
 import java.util.Collection;
17
 import java.util.Collection;
17
 import java.util.Collections;
18
 import java.util.Collections;
19
 public class Navigator extends ParentController {
20
 public class Navigator extends ParentController {
20
 
21
 
21
     private static final NoOpPromise NO_OP = new NoOpPromise();
22
     private static final NoOpPromise NO_OP = new NoOpPromise();
22
-    private final ModalStack modalStack = new ModalStack();
23
-	private ViewController root;
23
+    private final ModalStack modalStack = new ModalStack(new ModalCreator());
24
+    private ViewController root;
24
     private OverlayManager overlayManager = new OverlayManager();
25
     private OverlayManager overlayManager = new OverlayManager();
25
     private Options defaultOptions = new Options();
26
     private Options defaultOptions = new Options();
26
 
27
 
27
     public Navigator(final Activity activity) {
28
     public Navigator(final Activity activity) {
28
-		super(activity, "navigator" + CompatUtils.generateViewId());
29
-	}
29
+        super(activity, "navigator" + CompatUtils.generateViewId(), new Options());
30
+    }
31
+
32
+    @NonNull
33
+    @Override
34
+    protected ViewGroup createView() {
35
+        return new FrameLayout(getActivity());
36
+    }
30
 
37
 
31
     @NonNull
38
     @NonNull
32
-	@Override
33
-	protected ViewGroup createView() {
34
-		return new FrameLayout(getActivity());
35
-	}
36
-
37
-	@NonNull
38
-	@Override
39
-	public Collection<ViewController> getChildControllers() {
40
-		return root == null ? Collections.emptyList() : Collections.singletonList(root);
41
-	}
42
-
43
-	@Override
44
-	public boolean handleBack() {
45
-		return root != null && root.handleBack();
46
-	}
47
-
48
-	@Override
49
-	public void destroy() {
50
-		modalStack.dismissAll(NO_OP);
51
-		super.destroy();
52
-	}
53
-
54
-	public void setRoot(final ViewController viewController, Promise promise) {
55
-		if (root != null) {
56
-			root.destroy();
57
-		}
58
-
59
-		root = viewController;
60
-		getView().addView(viewController.getView());
39
+    @Override
40
+    public Collection<ViewController> getChildControllers() {
41
+        return root == null ? Collections.emptyList() : Collections.singletonList(root);
42
+    }
43
+
44
+    @Override
45
+    public boolean handleBack() {
46
+        return root != null && root.handleBack();
47
+    }
48
+
49
+    @Override
50
+    public void destroy() {
51
+        modalStack.dismissAll(NO_OP);
52
+        super.destroy();
53
+    }
54
+
55
+    @Override
56
+    public void sendOnNavigationButtonPressed(String buttonId) {
57
+
58
+    }
59
+
60
+    public void setRoot(final ViewController viewController, Promise promise) {
61
+        if (root != null) {
62
+            root.destroy();
63
+        }
64
+
65
+        root = viewController;
66
+        getView().addView(viewController.getView());
61
         promise.resolve(viewController.getId());
67
         promise.resolve(viewController.getId());
62
-	}
68
+    }
63
 
69
 
64
     public void setDefaultOptions(Options defaultOptions) {
70
     public void setDefaultOptions(Options defaultOptions) {
65
         this.defaultOptions = defaultOptions;
71
         this.defaultOptions = defaultOptions;
69
         return defaultOptions;
75
         return defaultOptions;
70
     }
76
     }
71
 
77
 
72
-	public void setOptions(final String componentId, Options options) {
73
-		ViewController target = findControllerById(componentId);
74
-		if (target instanceof NavigationOptionsListener) {
75
-			((NavigationOptionsListener) target).mergeOptions(options);
76
-		}
77
-		if (root instanceof NavigationOptionsListener) {
78
-			((NavigationOptionsListener) root).mergeOptions(options);
79
-		}
80
-	}
81
-
82
-	public void push(final String fromId, final ViewController viewController, Promise promise) {
83
-		ViewController from = findControllerById(fromId);
84
-		if (from != null) {
85
-		    from.performOnParentStack(stack -> ((StackController) stack).animatePush(viewController, promise));
86
-		}
87
-	}
88
-
89
-	void pop(final String fromId, Promise promise) {
90
-		ViewController from = findControllerById(fromId);
91
-		if (from != null) {
92
-		    from.performOnParentStack(stack -> ((StackController) stack).pop(promise));
93
-		}
94
-	}
95
-
96
-	public void popSpecific(final String id, Promise promise) {
97
-		ViewController from = findControllerById(id);
98
-		if (from != null) {
99
-		    from.performOnParentStack(stack -> ((StackController) stack).popSpecific(from, promise), () -> rejectPromise(promise));
100
-		} else {
101
-			rejectPromise(promise);
102
-		}
103
-	}
104
-
105
-	public void popToRoot(final String id, Promise promise) {
106
-		ViewController from = findControllerById(id);
107
-		if (from != null) {
108
-		    from.performOnParentStack(stack -> ((StackController) stack).popToRoot(promise));
109
-		}
110
-	}
111
-
112
-	public void popTo(final String componentId, Promise promise) {
113
-		ViewController target = findControllerById(componentId);
114
-		if (target != null) {
115
-		    target.performOnParentStack(stack -> ((StackController) stack).popTo(target, promise), () -> rejectPromise(promise));
116
-		} else {
117
-			rejectPromise(promise);
118
-		}
119
-	}
120
-
121
-	public void showModal(final ViewController viewController, Promise promise) {
122
-		modalStack.showModal(viewController, promise);
123
-	}
124
-
125
-	public void dismissModal(final String componentId, Promise promise) {
126
-		modalStack.dismissModal(componentId, promise);
127
-	}
128
-
129
-	public void dismissAllModals(Promise promise) {
130
-		modalStack.dismissAll(promise);
131
-	}
132
-
133
-	public void showOverlay(ViewController overlay) {
134
-        overlayManager.show(root.getView(), overlay);
135
-	}
136
-
137
-	public void dismissOverlay(final String componentId) {
138
-		overlayManager.dismiss(root.getView(), componentId);
139
-	}
140
-
141
-	static void rejectPromise(Promise promise) {
78
+    public void setOptions(final String componentId, Options options) {
79
+        ViewController target = findControllerById(componentId);
80
+        if (target instanceof NavigationOptionsListener) {
81
+            ((NavigationOptionsListener) target).mergeOptions(options);
82
+        }
83
+        if (root instanceof NavigationOptionsListener) {
84
+            ((NavigationOptionsListener) root).mergeOptions(options);
85
+        }
86
+    }
87
+
88
+    public void push(final String fromId, final ViewController viewController, Promise promise) {
89
+        ViewController from = findControllerById(fromId);
90
+        if (from != null) {
91
+            from.performOnParentStack(stack -> ((StackController) stack).animatePush(viewController, promise));
92
+        }
93
+    }
94
+
95
+    void pop(final String fromId, Promise promise) {
96
+        ViewController from = findControllerById(fromId);
97
+        if (from != null) {
98
+            from.performOnParentStack(stack -> ((StackController) stack).pop(promise));
99
+        }
100
+    }
101
+
102
+    public void popSpecific(final String id, Promise promise) {
103
+        ViewController from = findControllerById(id);
104
+        if (from != null) {
105
+            from.performOnParentStack(stack -> ((StackController) stack).popSpecific(from, promise), () -> rejectPromise(promise));
106
+        } else {
107
+            rejectPromise(promise);
108
+        }
109
+    }
110
+
111
+    public void popToRoot(final String id, Promise promise) {
112
+        ViewController from = findControllerById(id);
113
+        if (from != null) {
114
+            from.performOnParentStack(stack -> ((StackController) stack).popToRoot(promise));
115
+        }
116
+    }
117
+
118
+    public void popTo(final String componentId, Promise promise) {
119
+        ViewController target = findControllerById(componentId);
120
+        if (target != null) {
121
+            target.performOnParentStack(stack -> ((StackController) stack).popTo(target, promise), () -> rejectPromise(promise));
122
+        } else {
123
+            rejectPromise(promise);
124
+        }
125
+    }
126
+
127
+    public void showModal(final ViewController viewController, Promise promise) {
128
+        modalStack.showModal(viewController, promise);
129
+    }
130
+
131
+    public void dismissModal(final String componentId, Promise promise) {
132
+        modalStack.dismissModal(componentId, promise);
133
+    }
134
+
135
+    public void dismissAllModals(Promise promise) {
136
+        modalStack.dismissAll(promise);
137
+    }
138
+
139
+    public void showOverlay(ViewController overlay) {
140
+        overlayManager.show(getView(), overlay);
141
+    }
142
+
143
+    public void dismissOverlay(final String componentId) {
144
+        overlayManager.dismiss(getView(), componentId);
145
+    }
146
+
147
+    static void rejectPromise(Promise promise) {
142
         promise.reject(new Throwable("Nothing to pop"));
148
         promise.reject(new Throwable("Nothing to pop"));
143
-	}
149
+    }
144
 
150
 
145
     @Nullable
151
     @Nullable
146
     @Override
152
     @Override

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

1
 package com.reactnativenavigation.viewcontrollers;
1
 package com.reactnativenavigation.viewcontrollers;
2
 
2
 
3
 import android.app.Activity;
3
 import android.app.Activity;
4
+import android.support.annotation.CallSuper;
4
 import android.support.annotation.NonNull;
5
 import android.support.annotation.NonNull;
5
 import android.support.annotation.Nullable;
6
 import android.support.annotation.Nullable;
6
 import android.support.v4.view.ViewPager;
7
 import android.support.v4.view.ViewPager;
13
 
14
 
14
 public abstract class ParentController<T extends ViewGroup> extends ViewController {
15
 public abstract class ParentController<T extends ViewGroup> extends ViewController {
15
 
16
 
16
-	public ParentController(final Activity activity, final String id) {
17
-		super(activity, id);
17
+	public ParentController(final Activity activity, final String id, Options initialOptions) {
18
+		super(activity, id, initialOptions);
18
 	}
19
 	}
19
 
20
 
20
 	@NonNull
21
 	@NonNull
44
 		return null;
45
 		return null;
45
 	}
46
 	}
46
 
47
 
48
+	@Override
49
+    public boolean containsComponent(ReactComponent component) {
50
+        if (super.containsComponent(component)) {
51
+            return true;
52
+        }
53
+        for (ViewController child : getChildControllers()) {
54
+            if (child.containsComponent(component)) return true;
55
+        }
56
+        return false;
57
+    }
58
+
59
+    @CallSuper
47
     public void applyOptions(Options options, ReactComponent childComponent) {
60
     public void applyOptions(Options options, ReactComponent childComponent) {
61
+        mergeChildOptions(options);
62
+    }
48
 
63
 
64
+    private void mergeChildOptions(Options options) {
65
+        this.options = this.options.mergeWith(options);
49
     }
66
     }
50
 
67
 
51
 	@Override
68
 	@Override
56
 		}
73
 		}
57
 	}
74
 	}
58
 
75
 
76
+	@CallSuper
59
     void clearOptions() {
77
     void clearOptions() {
60
-
78
+	    applyOnParentController(parent -> ((ParentController) parent).clearOptions());
79
+        options = initialOptions.copy();
61
     }
80
     }
62
 
81
 
63
     public void setupTopTabsWithViewPager(ViewPager viewPager) {
82
     public void setupTopTabsWithViewPager(ViewPager viewPager) {
64
 
83
 
65
     }
84
     }
85
+
86
+    public void clearTopTabs() {
87
+
88
+    }
66
 }
89
 }

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

5
 import android.support.v4.widget.DrawerLayout;
5
 import android.support.v4.widget.DrawerLayout;
6
 import android.view.Gravity;
6
 import android.view.Gravity;
7
 import android.view.View;
7
 import android.view.View;
8
-import android.view.ViewGroup;
8
+
9
+import com.reactnativenavigation.parse.Options;
10
+import com.reactnativenavigation.presentation.NavigationOptionsListener;
11
+import com.reactnativenavigation.presentation.SideMenuOptionsPresenter;
12
+import com.reactnativenavigation.views.ReactComponent;
9
 
13
 
10
 import java.util.ArrayList;
14
 import java.util.ArrayList;
11
 import java.util.Collection;
15
 import java.util.Collection;
13
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
17
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
14
 import static android.widget.ListPopupWindow.WRAP_CONTENT;
18
 import static android.widget.ListPopupWindow.WRAP_CONTENT;
15
 
19
 
16
-public class SideMenuController extends ParentController {
20
+public class SideMenuController extends ParentController<DrawerLayout> implements NavigationOptionsListener {
17
 
21
 
18
 	private ViewController centerController;
22
 	private ViewController centerController;
19
 	private ViewController leftController;
23
 	private ViewController leftController;
20
 	private ViewController rightController;
24
 	private ViewController rightController;
21
 
25
 
22
-	public SideMenuController(final Activity activity, final String id) {
23
-		super(activity, id);
26
+	public SideMenuController(final Activity activity, final String id, Options initialOptions) {
27
+		super(activity, id, initialOptions);
24
 	}
28
 	}
25
 
29
 
26
 	@NonNull
30
 	@NonNull
27
 	@Override
31
 	@Override
28
-	protected ViewGroup createView() {
32
+	protected DrawerLayout createView() {
29
         return new DrawerLayout(getActivity());
33
         return new DrawerLayout(getActivity());
30
 	}
34
 	}
31
 
35
 
32
-	@NonNull
36
+    @Override
37
+    public void sendOnNavigationButtonPressed(String buttonId) {
38
+        centerController.sendOnNavigationButtonPressed(buttonId);
39
+    }
40
+
41
+    @NonNull
33
 	@Override
42
 	@Override
34
 	public Collection<ViewController> getChildControllers() {
43
 	public Collection<ViewController> getChildControllers() {
35
 		ArrayList<ViewController> children = new ArrayList<>();
44
 		ArrayList<ViewController> children = new ArrayList<>();
39
 		return children;
48
 		return children;
40
 	}
49
 	}
41
 
50
 
42
-	public void setCenterController(ViewController centerController) {
51
+    @Override
52
+    public void applyOptions(Options options, ReactComponent childComponent) {
53
+        super.applyOptions(options, childComponent);
54
+        applyOnParentController(parentController ->
55
+                ((ParentController) parentController).applyOptions(this.options, childComponent)
56
+        );
57
+    }
58
+
59
+    @Override
60
+    public void mergeOptions(Options options) {
61
+        this.options = this.options.mergeWith(options);
62
+        new SideMenuOptionsPresenter(getView()).present(this.options.sideMenuRootOptions);
63
+        this.options = this.options.copy().clearSideMenuOptions();
64
+    }
65
+
66
+    public void setCenterController(ViewController centerController) {
43
 		this.centerController = centerController;
67
 		this.centerController = centerController;
44
 		View childView = centerController.getView();
68
 		View childView = centerController.getView();
45
 		getView().addView(childView);
69
 		getView().addView(childView);

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

17
 import java.util.Collection;
17
 import java.util.Collection;
18
 import java.util.Iterator;
18
 import java.util.Iterator;
19
 
19
 
20
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
21
+
20
 public class StackController extends ParentController <StackLayout> {
22
 public class StackController extends ParentController <StackLayout> {
21
 
23
 
22
     private static final NoOpPromise NO_OP = new NoOpPromise();
24
     private static final NoOpPromise NO_OP = new NoOpPromise();
23
     private final IdStack<ViewController> stack = new IdStack<>();
25
     private final IdStack<ViewController> stack = new IdStack<>();
24
     private final NavigationAnimator animator;
26
     private final NavigationAnimator animator;
25
-    private StackLayout stackLayout;
26
 
27
 
27
-    public StackController(final Activity activity, String id) {
28
-		super(activity, id);
28
+    public StackController(final Activity activity, String id, Options initialOptions) {
29
+        super(activity, id, initialOptions);
29
         animator = new NavigationAnimator(activity);
30
         animator = new NavigationAnimator(activity);
30
     }
31
     }
31
 
32
 
32
     @RestrictTo(RestrictTo.Scope.TESTS)
33
     @RestrictTo(RestrictTo.Scope.TESTS)
33
     TopBar getTopBar() {
34
     TopBar getTopBar() {
34
-        return stackLayout.getTopBar();
35
+        return getView().getTopBar();
35
     }
36
     }
36
 
37
 
38
+    @RestrictTo(RestrictTo.Scope.TESTS)
39
+    StackLayout getStackLayout() {return getView();}
40
+
37
     @Override
41
     @Override
38
     public void applyOptions(Options options, ReactComponent component) {
42
     public void applyOptions(Options options, ReactComponent component) {
39
-        stackLayout.applyOptions(options, component);
43
+        super.applyOptions(options, component);
44
+        getView().applyOptions(this.options, component);
45
+        applyOnParentController(parentController ->
46
+                ((ParentController) parentController).applyOptions(this.options.copy().clearTopBarOptions(), component)
47
+        );
48
+        fabOptionsPresenter.applyOptions(options.fabOptions, component, getView());
49
+        animator.setOptions(options.animationsOptions);
40
     }
50
     }
41
 
51
 
42
     @Override
52
     @Override
43
     void clearOptions() {
53
     void clearOptions() {
44
-        stackLayout.clearOptions();
54
+        super.clearOptions();
55
+        getView().clearOptions();
45
     }
56
     }
46
 
57
 
47
     public void push(ViewController child, final Promise promise) {
58
     public void push(ViewController child, final Promise promise) {
50
         child.setParentController(this);
61
         child.setParentController(this);
51
         stack.push(child.getId(), child);
62
         stack.push(child.getId(), child);
52
         View enteringView = child.getView();
63
         View enteringView = child.getView();
53
-        getView().addView(enteringView);
64
+        getView().addView(enteringView, MATCH_PARENT, MATCH_PARENT);
54
 
65
 
55
         if (toRemove != null) {
66
         if (toRemove != null) {
56
             getView().removeView(toRemove.getView());
67
             getView().removeView(toRemove.getView());
59
     }
70
     }
60
 
71
 
61
     public void animatePush(final ViewController child, final Promise promise) {
72
     public void animatePush(final ViewController child, final Promise promise) {
62
-		final ViewController toRemove = stack.peek();
73
+        final ViewController toRemove = stack.peek();
63
 
74
 
64
 		child.setParentController(this);
75
 		child.setParentController(this);
65
 		stack.push(child.getId(), child);
76
 		stack.push(child.getId(), child);
66
 		View enteringView = child.getView();
77
 		View enteringView = child.getView();
67
-		getView().addView(enteringView);
78
+		getView().addView(enteringView, MATCH_PARENT, MATCH_PARENT);
68
 
79
 
69
-		if (toRemove != null) {
80
+        if (toRemove != null) {
70
             animator.animatePush(enteringView, () -> {
81
             animator.animatePush(enteringView, () -> {
71
                 getView().removeView(toRemove.getView());
82
                 getView().removeView(toRemove.getView());
72
                 promise.resolve(child.getId());
83
                 promise.resolve(child.getId());
73
             });
84
             });
74
-		} else {
75
-			promise.resolve(child.getId());
76
-		}
77
-	}
85
+        } else {
86
+            promise.resolve(child.getId());
87
+        }
88
+    }
78
 
89
 
79
     void pop(final Promise promise) {
90
     void pop(final Promise promise) {
80
         if (!canPop()) {
91
         if (!canPop()) {
82
             return;
93
             return;
83
         }
94
         }
84
 
95
 
85
-        final ViewController poppedTop = stack.pop();
86
-        ViewController newTop = stack.peek();
96
+        final ViewController exitingController = stack.pop();
97
+        final ViewController enteringController = stack.peek();
98
+        popInternal(exitingController, enteringController);
87
 
99
 
88
-        View enteringView = newTop.getView();
89
-        final View exitingView = poppedTop.getView();
90
-        getView().addView(enteringView, getView().getChildCount() - 1);
91
-
92
-        finishPopping(exitingView, poppedTop, promise);
100
+        finishPopping(exitingController.getView(), exitingController, promise);
93
     }
101
     }
94
 
102
 
95
-	private void animatePop(final Promise promise) {
103
+	void animatePop(final Promise promise) {
96
 		if (!canPop()) {
104
 		if (!canPop()) {
97
 			Navigator.rejectPromise(promise);
105
 			Navigator.rejectPromise(promise);
98
 			return;
106
 			return;
99
 		}
107
 		}
100
 
108
 
101
-		final ViewController poppedTop = stack.pop();
102
-		ViewController newTop = stack.peek();
103
-
104
-		View enteringView = newTop.getView();
105
-		final View exitingView = poppedTop.getView();
106
-		getView().addView(enteringView, getView().getChildCount() - 1);
109
+		final ViewController exitingController = stack.pop();
110
+        final ViewController enteringController = stack.peek();
111
+        popInternal(exitingController, enteringController);
107
 
112
 
108
-        animator.animatePop(exitingView, () -> finishPopping(exitingView, poppedTop, promise));
113
+        animator.animatePop(exitingController.getView(), () -> finishPopping(exitingController.getView(), exitingController, promise));
109
 	}
114
 	}
110
 
115
 
116
+    private void popInternal(ViewController disappearing, ViewController appearing) {
117
+        disappearing.onViewWillDisappear();
118
+        appearing.onViewWillAppear();
119
+        getView().onChildWillDisappear(disappearing.options, appearing.options);
120
+        getView().addView(appearing.getView(), getView().indexOfChild(disappearing.getView()));
121
+    }
122
+
111
     boolean canPop() {
123
     boolean canPop() {
112
         return stack.size() > 1;
124
         return stack.size() > 1;
113
     }
125
     }
180
         return false;
192
         return false;
181
 	}
193
 	}
182
 
194
 
195
+    @Override
196
+    public void sendOnNavigationButtonPressed(String buttonId) {
197
+        peek().sendOnNavigationButtonPressed(buttonId);
198
+    }
199
+
183
     @NonNull
200
     @NonNull
184
     @Override
201
     @Override
185
     protected StackLayout createView() {
202
     protected StackLayout createView() {
186
-        stackLayout = new StackLayout(getActivity());
187
-        return stackLayout;
203
+        return new StackLayout(getActivity(), this::sendOnNavigationButtonPressed);
188
     }
204
     }
189
 
205
 
190
 	@NonNull
206
 	@NonNull
195
 
211
 
196
     @Override
212
     @Override
197
     public void setupTopTabsWithViewPager(ViewPager viewPager) {
213
     public void setupTopTabsWithViewPager(ViewPager viewPager) {
198
-        stackLayout.setupTopTabsWithViewPager(viewPager);
214
+        getView().initTopTabs(viewPager);
215
+    }
216
+
217
+    @Override
218
+    public void clearTopTabs() {
219
+        getView().clearTopTabs();
199
     }
220
     }
200
 }
221
 }

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

1
 package com.reactnativenavigation.viewcontrollers;
1
 package com.reactnativenavigation.viewcontrollers;
2
 
2
 
3
 import android.app.Activity;
3
 import android.app.Activity;
4
+import android.support.annotation.CallSuper;
4
 import android.support.annotation.NonNull;
5
 import android.support.annotation.NonNull;
5
 import android.support.annotation.Nullable;
6
 import android.support.annotation.Nullable;
6
 import android.support.annotation.VisibleForTesting;
7
 import android.support.annotation.VisibleForTesting;
10
 import android.view.ViewTreeObserver;
11
 import android.view.ViewTreeObserver;
11
 
12
 
12
 import com.reactnativenavigation.parse.Options;
13
 import com.reactnativenavigation.parse.Options;
14
+import com.reactnativenavigation.presentation.FabOptionsPresenter;
13
 import com.reactnativenavigation.utils.CompatUtils;
15
 import com.reactnativenavigation.utils.CompatUtils;
14
 import com.reactnativenavigation.utils.StringUtils;
16
 import com.reactnativenavigation.utils.StringUtils;
15
 import com.reactnativenavigation.utils.Task;
17
 import com.reactnativenavigation.utils.Task;
29
         boolean onViewDisappear(View view);
31
         boolean onViewDisappear(View view);
30
     }
32
     }
31
 
33
 
34
+    Options initialOptions;
32
     public Options options;
35
     public Options options;
33
 
36
 
34
     private final Activity activity;
37
     private final Activity activity;
38
     private boolean isShown;
41
     private boolean isShown;
39
     private boolean isDestroyed;
42
     private boolean isDestroyed;
40
     private ViewVisibilityListener viewVisibilityListener = new ViewVisibilityListenerAdapter();
43
     private ViewVisibilityListener viewVisibilityListener = new ViewVisibilityListenerAdapter();
44
+    FabOptionsPresenter fabOptionsPresenter;
41
 
45
 
42
-    public ViewController(Activity activity, String id) {
46
+    public ViewController(Activity activity, String id, Options initialOptions) {
43
         this.activity = activity;
47
         this.activity = activity;
44
         this.id = id;
48
         this.id = id;
49
+        fabOptionsPresenter = new FabOptionsPresenter();
50
+        this.initialOptions = initialOptions;
51
+        options = initialOptions.copy();
45
     }
52
     }
46
 
53
 
47
     protected abstract T createView();
54
     protected abstract T createView();
125
         return isSameId(id) ? this : null;
132
         return isSameId(id) ? this : null;
126
     }
133
     }
127
 
134
 
135
+    public boolean containsComponent(ReactComponent component) {
136
+        return getView().equals(component);
137
+    }
138
+
139
+    public void onViewWillAppear() {
140
+
141
+    }
142
+
128
     public void onViewAppeared() {
143
     public void onViewAppeared() {
129
         isShown = true;
144
         isShown = true;
145
+        applyOptions(options);
130
         applyOnParentController(parentController -> {
146
         applyOnParentController(parentController -> {
131
             parentController.clearOptions();
147
             parentController.clearOptions();
132
-            parentController.applyOptions(options, (ReactComponent) getView());
148
+            if (getView() instanceof ReactComponent) parentController.applyOptions(options, (ReactComponent) getView());
133
         });
149
         });
134
     }
150
     }
135
 
151
 
152
+    public void onViewWillDisappear() {
153
+
154
+    }
155
+
136
     public void onViewDisappear() {
156
     public void onViewDisappear() {
137
         isShown = false;
157
         isShown = false;
138
     }
158
     }
139
 
159
 
160
+    @CallSuper
140
     public void destroy() {
161
     public void destroy() {
141
         if (isShown) {
162
         if (isShown) {
142
             isShown = false;
163
             isShown = false;
170
         }
191
         }
171
     }
192
     }
172
 
193
 
194
+    public abstract void sendOnNavigationButtonPressed(String buttonId);
195
+
173
     protected boolean isViewShown() {
196
     protected boolean isViewShown() {
174
         return !isDestroyed && getView().isShown();
197
         return !isDestroyed && getView().isShown();
175
     }
198
     }

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

1
+package com.reactnativenavigation.viewcontrollers.bottomtabs;
2
+
3
+import android.support.annotation.IntRange;
4
+
5
+import com.reactnativenavigation.viewcontrollers.ViewController;
6
+import com.reactnativenavigation.views.ReactComponent;
7
+
8
+import java.util.List;
9
+
10
+public class BottomTabFinder {
11
+    private List<ViewController> tabs;
12
+
13
+    @IntRange(from = -1)
14
+    public int findByComponent(ReactComponent component) {
15
+        for (int i = 0; i < tabs.size(); i++) {
16
+            if (tabs.get(i).containsComponent(component)) {
17
+                return i;
18
+            }
19
+        }
20
+        return -1;
21
+    }
22
+
23
+    @IntRange(from = -1)
24
+    public int findByControllerId(String id) {
25
+        for (int i = 0; i < tabs.size(); i++) {
26
+            if (tabs.get(i).findControllerById(id) != null) {
27
+                return i;
28
+            }
29
+        }
30
+        return -1;
31
+    }
32
+
33
+    void setTabs(List<ViewController> tabs) {
34
+        this.tabs = tabs;
35
+    }
36
+}

+ 152
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java View File

1
+package com.reactnativenavigation.viewcontrollers.bottomtabs;
2
+
3
+import android.app.Activity;
4
+import android.graphics.drawable.Drawable;
5
+import android.support.annotation.NonNull;
6
+import android.view.ViewGroup;
7
+import android.widget.RelativeLayout;
8
+
9
+import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
10
+import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
11
+import com.reactnativenavigation.parse.BottomTabOptions;
12
+import com.reactnativenavigation.parse.Options;
13
+import com.reactnativenavigation.presentation.BottomTabsOptionsPresenter;
14
+import com.reactnativenavigation.presentation.NavigationOptionsListener;
15
+import com.reactnativenavigation.utils.ImageLoader;
16
+import com.reactnativenavigation.viewcontrollers.ParentController;
17
+import com.reactnativenavigation.viewcontrollers.ViewController;
18
+import com.reactnativenavigation.views.BottomTabs;
19
+import com.reactnativenavigation.views.ReactComponent;
20
+
21
+import java.util.ArrayList;
22
+import java.util.Collection;
23
+import java.util.List;
24
+
25
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
26
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
27
+import static android.widget.RelativeLayout.ABOVE;
28
+import static android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM;
29
+
30
+public class BottomTabsController extends ParentController implements AHBottomNavigation.OnTabSelectedListener, NavigationOptionsListener {
31
+	private BottomTabs bottomTabs;
32
+	private List<ViewController> tabs = new ArrayList<>();
33
+    private ImageLoader imageLoader;
34
+    private BottomTabFinder bottomTabFinder = new BottomTabFinder();
35
+
36
+    public BottomTabsController(final Activity activity, ImageLoader imageLoader, final String id, Options initialOptions) {
37
+		super(activity, id, initialOptions);
38
+        this.imageLoader = imageLoader;
39
+    }
40
+
41
+	@NonNull
42
+	@Override
43
+	protected ViewGroup createView() {
44
+		RelativeLayout root = new RelativeLayout(getActivity());
45
+		bottomTabs = new BottomTabs(getActivity());
46
+        bottomTabs.setOnTabSelectedListener(this);
47
+		RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
48
+		lp.addRule(ALIGN_PARENT_BOTTOM);
49
+		root.addView(bottomTabs, lp);
50
+		return root;
51
+	}
52
+
53
+    @Override
54
+    public void applyOptions(Options options) {
55
+        super.applyOptions(options);
56
+        new BottomTabsOptionsPresenter(bottomTabs, bottomTabFinder).present(options);
57
+    }
58
+
59
+    @Override
60
+    public void applyOptions(Options options, ReactComponent childComponent) {
61
+        super.applyOptions(options, childComponent);
62
+        int tabIndex = bottomTabFinder.findByComponent(childComponent);
63
+        if (tabIndex >= 0) new BottomTabsOptionsPresenter(bottomTabs, bottomTabFinder).present(this.options, tabIndex);
64
+        applyOnParentController(parentController ->
65
+                ((ParentController) parentController).applyOptions(this.options.copy().clearBottomTabsOptions().clearBottomTabOptions(), childComponent)
66
+        );
67
+    }
68
+
69
+    @Override
70
+	public boolean handleBack() {
71
+		return !tabs.isEmpty() && tabs.get(bottomTabs.getCurrentItem()).handleBack();
72
+	}
73
+
74
+    @Override
75
+    public void sendOnNavigationButtonPressed(String buttonId) {
76
+        getCurrentTab().sendOnNavigationButtonPressed(buttonId);
77
+    }
78
+
79
+    private ViewController getCurrentTab() {
80
+        return tabs.get(bottomTabs.getCurrentItem());
81
+    }
82
+
83
+    @Override
84
+    public boolean onTabSelected(int index, boolean wasSelected) {
85
+        if (wasSelected) return false;
86
+        selectTabAtIndex(index);
87
+        return true;
88
+	}
89
+	
90
+	public void setTabs(final List<ViewController> tabs) {
91
+		if (tabs.size() > 5) {
92
+			throw new RuntimeException("Too many tabs!");
93
+		}
94
+		this.tabs = tabs;
95
+        bottomTabFinder.setTabs(tabs);
96
+        getView();
97
+		for (int i = 0; i < tabs.size(); i++) {
98
+		    tabs.get(i).setParentController(this);
99
+			createTab(i, tabs.get(i).options.bottomTabOptions);
100
+		}
101
+		selectTabAtIndex(0);
102
+	}
103
+
104
+	private void createTab(int index, final BottomTabOptions tabOptions) {
105
+	    if (!tabOptions.icon.hasValue()) {
106
+            throw new RuntimeException("BottomTab must have an icon");
107
+        }
108
+        imageLoader.loadIcon(getActivity(), tabOptions.icon.get(), new ImageLoader.ImageLoadingListener() {
109
+            @Override
110
+            public void onComplete(@NonNull Drawable drawable) {
111
+                AHBottomNavigationItem item = new AHBottomNavigationItem(tabOptions.title.get(""), drawable);
112
+                bottomTabs.addItem(item);
113
+                bottomTabs.post(() -> bottomTabs.setTabTag(index, tabOptions.testId));
114
+            }
115
+
116
+            @Override
117
+            public void onError(Throwable error) {
118
+                error.printStackTrace();
119
+            }
120
+        });
121
+
122
+        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
123
+        params.addRule(ABOVE, bottomTabs.getId());
124
+	}
125
+
126
+    public int getSelectedIndex() {
127
+		return bottomTabs.getCurrentItem();
128
+	}
129
+
130
+	@NonNull
131
+	@Override
132
+	public Collection<ViewController> getChildControllers() {
133
+		return tabs;
134
+	}
135
+
136
+	@Override
137
+	public void mergeOptions(Options options) {
138
+        this.options = this.options.mergeWith(options);
139
+        new BottomTabsOptionsPresenter(bottomTabs, bottomTabFinder).present(this.options);
140
+    }
141
+
142
+    public void selectTabAtIndex(final int newIndex) {
143
+        getView().removeView(getCurrentView());
144
+        bottomTabs.setCurrentItem(newIndex, false);
145
+        getView().addView(getCurrentView());
146
+    }
147
+
148
+    @NonNull
149
+    private ViewGroup getCurrentView() {
150
+        return tabs.get(bottomTabs.getCurrentItem()).getView();
151
+    }
152
+}

+ 55
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/Modal.java View File

1
+package com.reactnativenavigation.viewcontrollers.modal;
2
+
3
+import android.app.Dialog;
4
+import android.content.DialogInterface;
5
+import android.view.KeyEvent;
6
+import android.view.View;
7
+
8
+import com.reactnativenavigation.R;
9
+import com.reactnativenavigation.viewcontrollers.ViewController;
10
+
11
+import static android.view.View.MeasureSpec.EXACTLY;
12
+import static android.view.View.MeasureSpec.makeMeasureSpec;
13
+
14
+public class Modal implements DialogInterface.OnKeyListener {
15
+    public final ViewController viewController;
16
+    private final Dialog dialog;
17
+
18
+    public Modal(final ViewController viewController) {
19
+        this.viewController = viewController;
20
+        dialog = new Dialog(viewController.getActivity(), R.style.Modal);
21
+        dialog.setOnKeyListener(this);
22
+    }
23
+
24
+    public void show() {
25
+        preMeasureView();
26
+        dialog.setContentView(viewController.getView());
27
+        dialog.show();
28
+    }
29
+
30
+    public void dismiss() {
31
+        dialog.dismiss();
32
+    }
33
+
34
+    public boolean containsDeepComponentId(String componentId) {
35
+        return viewController.findControllerById(componentId) != null;
36
+    }
37
+
38
+    private void preMeasureView() {
39
+        View decorView = viewController.getActivity().getWindow().getDecorView();
40
+        viewController.getView().measure(makeMeasureSpec(decorView.getMeasuredWidth(), EXACTLY), makeMeasureSpec(decorView.getMeasuredHeight(), EXACTLY));
41
+    }
42
+
43
+    @Override
44
+    public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
45
+        if (keyCode == KeyEvent.KEYCODE_BACK) {
46
+            if (event.getAction() == KeyEvent.ACTION_UP) {
47
+                if (viewController.handleBack()) {
48
+                    return true;
49
+                }
50
+                dialog.dismiss();
51
+            }
52
+        }
53
+        return false;
54
+    }
55
+}

+ 9
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalCreator.java View File

1
+package com.reactnativenavigation.viewcontrollers.modal;
2
+
3
+import com.reactnativenavigation.viewcontrollers.ViewController;
4
+
5
+public class ModalCreator {
6
+    public Modal create(ViewController viewController) {
7
+        return new Modal(viewController);
8
+    }
9
+}

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

21
 
21
 
22
     private List<ViewController> tabs;
22
     private List<ViewController> tabs;
23
     private TopTabsLayoutCreator viewCreator;
23
     private TopTabsLayoutCreator viewCreator;
24
-    private Options options;
25
 
24
 
26
     public TopTabsController(Activity activity, String id, List<ViewController> tabs, TopTabsLayoutCreator viewCreator, Options options) {
25
     public TopTabsController(Activity activity, String id, List<ViewController> tabs, TopTabsLayoutCreator viewCreator, Options options) {
27
-        super(activity, id);
26
+        super(activity, id, options);
28
         this.viewCreator = viewCreator;
27
         this.viewCreator = viewCreator;
29
-        this.options = options;
30
         this.tabs = tabs;
28
         this.tabs = tabs;
31
         for (ViewController tab : tabs) {
29
         for (ViewController tab : tabs) {
32
             tab.setParentController(this);
30
             tab.setParentController(this);
59
         performOnCurrentTab(ViewController::onViewAppeared);
57
         performOnCurrentTab(ViewController::onViewAppeared);
60
     }
58
     }
61
 
59
 
60
+    @Override
61
+    public void onViewWillDisappear() {
62
+        super.onViewWillDisappear();
63
+    }
64
+
62
     @Override
65
     @Override
63
     public void onViewDisappear() {
66
     public void onViewDisappear() {
64
         performOnCurrentTab(ViewController::onViewDisappear);
67
         performOnCurrentTab(ViewController::onViewDisappear);
68
+        applyOnParentController(parentController -> ((ParentController) parentController).clearTopTabs());
69
+    }
70
+
71
+    @Override
72
+    public void sendOnNavigationButtonPressed(String buttonId) {
73
+        performOnCurrentTab(tab -> tab.sendOnNavigationButtonPressed(buttonId));
65
     }
74
     }
66
 
75
 
67
     @Override
76
     @Override
71
 
80
 
72
     @Override
81
     @Override
73
     public void applyOptions(Options options, ReactComponent childComponent) {
82
     public void applyOptions(Options options, ReactComponent childComponent) {
74
-        applyOnParentController(parentController -> ((ParentController) parentController).applyOptions(options, childComponent));
83
+        super.applyOptions(options, childComponent);
84
+        applyOnParentController(parentController -> {
85
+                Options opt = this.options.copy();
86
+                ((ParentController) parentController).applyOptions(opt.clearTopTabOptions().clearTopTabsOptions(), childComponent);
87
+            }
88
+        );
75
     }
89
     }
76
 
90
 
77
     @Override
91
     @Override

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

1
+package com.reactnativenavigation.views;
2
+
3
+import android.annotation.SuppressLint;
4
+import android.content.Context;
5
+import android.support.annotation.IntRange;
6
+
7
+import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
8
+import com.reactnativenavigation.parse.params.Text;
9
+import com.reactnativenavigation.utils.CompatUtils;
10
+
11
+@SuppressLint("ViewConstructor")
12
+public class BottomTabs extends AHBottomNavigation {
13
+    public BottomTabs(Context context) {
14
+        super(context);
15
+        setId(CompatUtils.generateViewId());
16
+        setContentDescription("BottomTabs");
17
+    }
18
+
19
+    public void setTabTag(int index, Text testId) {
20
+        if (!testId.hasValue()) return;
21
+        if (testId.hasValue()) getViewAtPosition(index).setTag(testId.get());
22
+        if (testId.hasValue()) getViewAtPosition(index).setContentDescription(testId.get());
23
+    }
24
+
25
+    public void setBadge(int bottomTabIndex, Text badge) {
26
+        setNotification(badge.get(), bottomTabIndex);
27
+    }
28
+
29
+    @Override
30
+    public void setCurrentItem(@IntRange(from = 0) int position) {
31
+        super.setCurrentItem(position);
32
+    }
33
+}

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

25
 		super(context);
25
 		super(context);
26
 		this.reactView = reactView;
26
 		this.reactView = reactView;
27
         addView(reactView.asView(), MATCH_PARENT, MATCH_PARENT);
27
         addView(reactView.asView(), MATCH_PARENT, MATCH_PARENT);
28
+        setContentDescription("ComponentLayout");
28
         touchDelegate = new OverlayTouchDelegate(reactView);
29
         touchDelegate = new OverlayTouchDelegate(reactView);
29
     }
30
     }
30
 
31
 
55
 
56
 
56
     @Override
57
     @Override
57
     public void applyOptions(Options options) {
58
     public void applyOptions(Options options) {
58
-        touchDelegate.setInterceptTouchOutside(options.overlayOptions.interceptTouchOutside == Options.BooleanOptions.True);
59
+        touchDelegate.setInterceptTouchOutside(options.overlayOptions.interceptTouchOutside.isTrue());
59
     }
60
     }
60
 
61
 
61
     @Override
62
     @Override
75
 
76
 
76
     @Override
77
     @Override
77
     public void drawBehindTopBar() {
78
     public void drawBehindTopBar() {
78
-        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
79
-        layoutParams.removeRule(BELOW);
80
-        reactView.asView().setLayoutParams(layoutParams);
79
+        if (getParent() instanceof RelativeLayout) {
80
+            RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
81
+            layoutParams.removeRule(BELOW);
82
+            setLayoutParams(layoutParams);
83
+        }
81
     }
84
     }
82
 
85
 
83
     @Override
86
     @Override
84
     public void drawBelowTopBar(TopBar topBar) {
87
     public void drawBelowTopBar(TopBar topBar) {
85
-        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
86
-        layoutParams.addRule(BELOW, topBar.getId());
87
-        reactView.asView().setLayoutParams(layoutParams);
88
+        if (getParent() instanceof RelativeLayout) {
89
+            RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
90
+            layoutParams.addRule(BELOW, topBar.getId());
91
+            setLayoutParams(layoutParams);
92
+        }
88
     }
93
     }
89
 
94
 
90
     @Override
95
     @Override
94
 
99
 
95
     @Override
100
     @Override
96
     public boolean onInterceptTouchEvent(MotionEvent ev) {
101
     public boolean onInterceptTouchEvent(MotionEvent ev) {
97
-        return touchDelegate.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev);
102
+        return touchDelegate.onInterceptTouchEvent(ev);
98
     }
103
     }
99
 }
104
 }

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

1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+import android.graphics.drawable.Drawable;
5
+import android.support.annotation.NonNull;
6
+
7
+import com.github.clans.fab.FloatingActionButton;
8
+import com.reactnativenavigation.anim.FabAnimator;
9
+import com.reactnativenavigation.anim.FabCollapseBehaviour;
10
+import com.reactnativenavigation.interfaces.ScrollEventListener;
11
+import com.reactnativenavigation.utils.ImageLoader;
12
+
13
+
14
+public class Fab extends FloatingActionButton implements FabAnimator {
15
+
16
+    private String id = "";
17
+    private FabCollapseBehaviour collapseBehaviour;
18
+
19
+    public Fab(Context context, String id) {
20
+        super(context);
21
+        collapseBehaviour = new FabCollapseBehaviour(this);
22
+        this.id = id;
23
+    }
24
+
25
+    public void applyIcon(String icon) {
26
+        new ImageLoader().loadIcon(getContext(), icon, new ImageLoader.ImageLoadingListener() {
27
+            @Override
28
+            public void onComplete(@NonNull Drawable drawable) {
29
+                setImageDrawable(drawable);
30
+            }
31
+
32
+            @Override
33
+            public void onError(Throwable error) {
34
+                error.printStackTrace();
35
+            }
36
+        });
37
+    }
38
+
39
+    @Override
40
+    public boolean equals(Object o) {
41
+        if (this == o) return true;
42
+        if (o == null || getClass() != o.getClass()) return false;
43
+
44
+        Fab fab = (Fab) o;
45
+
46
+        return id.equals(fab.id);
47
+    }
48
+
49
+    @Override
50
+    public int hashCode() {
51
+        return id.hashCode();
52
+    }
53
+
54
+    @Override
55
+    public void show() {
56
+        show(true);
57
+    }
58
+
59
+    @Override
60
+    public void hide() {
61
+        hide(true);
62
+    }
63
+
64
+    public void enableCollapse(@NonNull ScrollEventListener scrollEventListener) {
65
+        collapseBehaviour.enableCollapse(scrollEventListener);
66
+    }
67
+
68
+    public void disableCollapse() {
69
+        collapseBehaviour.disableCollapse();
70
+    }
71
+
72
+    public String getFabId() {
73
+        return id;
74
+    }
75
+}

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

1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+
5
+import com.github.clans.fab.FloatingActionMenu;
6
+import com.reactnativenavigation.anim.FabAnimator;
7
+import com.reactnativenavigation.anim.FabCollapseBehaviour;
8
+import com.reactnativenavigation.interfaces.ScrollEventListener;
9
+
10
+import java.util.HashSet;
11
+
12
+
13
+public class FabMenu extends FloatingActionMenu implements FabAnimator {
14
+
15
+    private String id = "";
16
+    private HashSet<Fab> actions = new HashSet<>();
17
+
18
+    private FabCollapseBehaviour collapseBehaviour;
19
+
20
+    public FabMenu(Context context, String id) {
21
+        super(context);
22
+        this.id = id;
23
+        collapseBehaviour = new FabCollapseBehaviour(this);
24
+        onFinishInflate();
25
+        setOnMenuButtonClickListener(v -> toggle(true));
26
+    }
27
+
28
+    @Override
29
+    public void show() {
30
+        showMenu(true);
31
+    }
32
+
33
+    @Override
34
+    public void hide() {
35
+        hideMenu(true);
36
+    }
37
+
38
+    public void enableCollapse(ScrollEventListener scrollEventListener) {
39
+        collapseBehaviour.enableCollapse(scrollEventListener);
40
+    }
41
+
42
+    public void disableCollapse() {
43
+        collapseBehaviour.disableCollapse();
44
+    }
45
+
46
+    public HashSet<Fab> getActions() {
47
+        return actions;
48
+    }
49
+
50
+    public String getFabId() {
51
+        return id;
52
+    }
53
+}

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

1
 package com.reactnativenavigation.views;
1
 package com.reactnativenavigation.views;
2
 
2
 
3
+import android.annotation.SuppressLint;
3
 import android.content.Context;
4
 import android.content.Context;
4
 import android.support.annotation.RestrictTo;
5
 import android.support.annotation.RestrictTo;
5
 import android.support.v4.view.ViewPager;
6
 import android.support.v4.view.ViewPager;
12
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
13
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
13
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
14
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
14
 
15
 
15
-public class StackLayout extends RelativeLayout implements TitleBarButton.OnClickListener {
16
+@SuppressLint("ViewConstructor")
17
+public class StackLayout extends RelativeLayout {
18
+    private TopBar topBar;
16
 
19
 
17
-    private final TopBar topBar;
18
-
19
-    public StackLayout(Context context) {
20
+    public StackLayout(Context context, TitleBarButton.OnClickListener topBarButtonClickListener) {
20
         super(context);
21
         super(context);
21
-        topBar = new TopBar(context, this);
22
+        topBar = new TopBar(context, topBarButtonClickListener, this);
22
         topBar.setId(CompatUtils.generateViewId());
23
         topBar.setId(CompatUtils.generateViewId());
23
         createLayout();
24
         createLayout();
25
+        setContentDescription("StackLayout");
24
     }
26
     }
25
 
27
 
26
     void createLayout() {
28
     void createLayout() {
27
         addView(topBar, MATCH_PARENT, WRAP_CONTENT);
29
         addView(topBar, MATCH_PARENT, WRAP_CONTENT);
28
     }
30
     }
29
 
31
 
30
-    @Override
31
-    public void onPress(String buttonId) {
32
-
33
-    }
34
-
35
     public void applyOptions(Options options, ReactComponent component) {
32
     public void applyOptions(Options options, ReactComponent component) {
36
         new OptionsPresenter(topBar, component).applyOptions(options);
33
         new OptionsPresenter(topBar, component).applyOptions(options);
37
     }
34
     }
38
 
35
 
39
-    @RestrictTo(RestrictTo.Scope.TESTS)
40
-    public TopBar getTopBar() {
41
-        return topBar;
36
+    public void onChildWillDisappear(Options disappearing, Options appearing) {
37
+        new OptionsPresenter(topBar).onChildWillDisappear(disappearing, appearing);
42
     }
38
     }
43
 
39
 
44
     public void clearOptions() {
40
     public void clearOptions() {
45
         topBar.clear();
41
         topBar.clear();
46
     }
42
     }
47
 
43
 
48
-    public void setupTopTabsWithViewPager(ViewPager viewPager) {
49
-        topBar.setupTopTabsWithViewPager(viewPager);
44
+    public void initTopTabs(ViewPager viewPager) {
45
+        topBar.initTopTabs(viewPager);
46
+    }
47
+
48
+    public void clearTopTabs() {
49
+        topBar.clearTopTabs();
50
+    }
51
+
52
+    @RestrictTo(RestrictTo.Scope.TESTS)
53
+    public TopBar getTopBar() {
54
+        return topBar;
55
+    }
56
+
57
+    @RestrictTo(RestrictTo.Scope.TESTS)
58
+    public void setTopBar(TopBar topBar) {
59
+        this.topBar = topBar;
50
     }
60
     }
51
 }
61
 }

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

4
 import android.graphics.Color;
4
 import android.graphics.Color;
5
 import android.graphics.drawable.Drawable;
5
 import android.graphics.drawable.Drawable;
6
 import android.support.annotation.NonNull;
6
 import android.support.annotation.NonNull;
7
+import android.support.v7.widget.ActionMenuView;
7
 import android.support.v7.widget.Toolbar;
8
 import android.support.v7.widget.Toolbar;
8
 import android.text.Spannable;
9
 import android.text.Spannable;
9
 import android.text.SpannableString;
10
 import android.text.SpannableString;
12
 import android.view.Menu;
13
 import android.view.Menu;
13
 import android.view.MenuItem;
14
 import android.view.MenuItem;
14
 import android.view.View;
15
 import android.view.View;
16
+import android.widget.ImageButton;
15
 import android.widget.TextView;
17
 import android.widget.TextView;
16
 
18
 
17
-import com.reactnativenavigation.parse.Button;
18
-import com.reactnativenavigation.parse.Options;
19
-import com.reactnativenavigation.utils.ImageUtils;
19
+import com.reactnativenavigation.parse.params.Button;
20
+import com.reactnativenavigation.parse.params.Text;
21
+import com.reactnativenavigation.utils.ArrayUtils;
22
+import com.reactnativenavigation.utils.ImageLoader;
20
 import com.reactnativenavigation.utils.UiUtils;
23
 import com.reactnativenavigation.utils.UiUtils;
24
+import com.reactnativenavigation.utils.ViewUtils;
21
 
25
 
22
 import java.util.ArrayList;
26
 import java.util.ArrayList;
27
+import java.util.List;
23
 
28
 
24
 public class TitleBarButton implements MenuItem.OnMenuItemClickListener {
29
 public class TitleBarButton implements MenuItem.OnMenuItemClickListener {
25
     public interface OnClickListener {
30
     public interface OnClickListener {
40
 	void addToMenu(Context context, final Menu menu) {
45
 	void addToMenu(Context context, final Menu menu) {
41
 		MenuItem menuItem = menu.add(button.title.get(""));
46
 		MenuItem menuItem = menu.add(button.title.get(""));
42
 		menuItem.setShowAsAction(button.showAsAction);
47
 		menuItem.setShowAsAction(button.showAsAction);
43
-		menuItem.setEnabled(button.disabled != Options.BooleanOptions.True);
48
+		menuItem.setEnabled(button.enabled.isTrueOrUndefined());
44
 		menuItem.setOnMenuItemClickListener(this);
49
 		menuItem.setOnMenuItemClickListener(this);
45
 
50
 
46
 		if (hasIcon()) {
51
 		if (hasIcon()) {
49
 			setTextColor();
54
 			setTextColor();
50
 			setFontSize(menuItem);
55
 			setFontSize(menuItem);
51
 		}
56
 		}
52
-	}
53
 
57
 
54
-	void applyNavigationIcon(Context context) {
58
+        setTestId(button.testId);
59
+    }
60
+
61
+    void applyNavigationIcon(Context context) {
55
 		if (!hasIcon()) {
62
 		if (!hasIcon()) {
56
 			Log.w("RNN", "Left button needs to have an icon");
63
 			Log.w("RNN", "Left button needs to have an icon");
57
 			return;
64
 			return;
58
 		}
65
 		}
59
 
66
 
60
-		ImageUtils.loadIcon(context, button.icon.get(), new ImageUtils.ImageLoadingListener() {
67
+		new ImageLoader().loadIcon(context, button.icon.get(), new ImageLoader.ImageLoadingListener() {
61
 			@Override
68
 			@Override
62
 			public void onComplete(@NonNull Drawable drawable) {
69
 			public void onComplete(@NonNull Drawable drawable) {
63
 				icon = drawable;
70
 				icon = drawable;
64
                 setIconColor();
71
                 setIconColor();
65
                 setNavigationClickListener();
72
                 setNavigationClickListener();
66
                 toolbar.setNavigationIcon(icon);
73
                 toolbar.setNavigationIcon(icon);
67
-			}
74
+                setLeftButtonTestId();
75
+            }
68
 
76
 
69
 			@Override
77
 			@Override
70
 			public void onError(Throwable error) {
78
 			public void onError(Throwable error) {
73
 		});
81
 		});
74
 	}
82
 	}
75
 
83
 
76
-	private void applyIcon(Context context, final MenuItem menuItem) {
77
-		ImageUtils.loadIcon(context, button.icon.get(), new ImageUtils.ImageLoadingListener() {
84
+    private void setLeftButtonTestId() {
85
+        if (!button.testId.hasValue()) return;
86
+        toolbar.post(() -> {
87
+            ImageButton leftButton = ViewUtils.findChildByClass(toolbar, ImageButton.class);
88
+            if (leftButton != null) {
89
+                leftButton.setTag(button.testId.get());
90
+            }
91
+        });
92
+    }
93
+
94
+    private void applyIcon(Context context, final MenuItem menuItem) {
95
+        new ImageLoader().loadIcon(context, button.icon.get(), new ImageLoader.ImageLoadingListener() {
78
 			@Override
96
 			@Override
79
 			public void onComplete(@NonNull Drawable drawable) {
97
 			public void onComplete(@NonNull Drawable drawable) {
80
 				icon = drawable;
98
 				icon = drawable;
90
 	}
108
 	}
91
 
109
 
92
 	private void setIconColor() {
110
 	private void setIconColor() {
93
-		if (button.disabled == Options.BooleanOptions.False || button.disabled == Options.BooleanOptions.NoValue) {
111
+		if (button.enabled.isTrueOrUndefined()) {
94
 			UiUtils.tintDrawable(icon, button.buttonColor);
112
 			UiUtils.tintDrawable(icon, button.buttonColor);
95
 			return;
113
 			return;
96
 		}
114
 		}
97
-
98
-		if (button.disableIconTint == Options.BooleanOptions.True) {
115
+		if (button.disableIconTint.isTrue()) {
99
 			UiUtils.tintDrawable(icon, button.buttonColor);
116
 			UiUtils.tintDrawable(icon, button.buttonColor);
100
 		} else {
117
 		} else {
101
 			UiUtils.tintDrawable(icon, Color.LTGRAY);
118
 			UiUtils.tintDrawable(icon, Color.LTGRAY);
104
 
121
 
105
 	private void setTextColor() {
122
 	private void setTextColor() {
106
 		UiUtils.runOnPreDrawOnce(this.toolbar, () -> {
123
 		UiUtils.runOnPreDrawOnce(this.toolbar, () -> {
107
-            ArrayList<View> outViews = findActualTextViewInMenuByLabel();
124
+            ArrayList<View> outViews = findActualTextViewInMenuByText();
108
             setTextColorForFoundButtonViews(outViews);
125
             setTextColorForFoundButtonViews(outViews);
109
         });
126
         });
110
 	}
127
 	}
125
 		return true;
142
 		return true;
126
 	}
143
 	}
127
 
144
 
128
-	@NonNull
129
-	private ArrayList<View> findActualTextViewInMenuByLabel() {
130
-		ArrayList<View> outViews = new ArrayList<>();
131
-		this.toolbar.findViewsWithText(outViews, button.title.get(), View.FIND_VIEWS_WITH_TEXT);
132
-		return outViews;
133
-	}
134
-
135
 	private void setTextColorForFoundButtonViews(ArrayList<View> buttons) {
145
 	private void setTextColorForFoundButtonViews(ArrayList<View> buttons) {
136
 		for (View button : buttons) {
146
 		for (View button : buttons) {
137
 			((TextView) button).setTextColor(this.button.buttonColor);
147
 			((TextView) button).setTextColor(this.button.buttonColor);
139
 	}
149
 	}
140
 
150
 
141
 	private boolean hasIcon() {
151
 	private boolean hasIcon() {
142
-		return button.icon != null;
152
+		return button.icon.hasValue();
143
 	}
153
 	}
154
+
155
+    private void setTestId(Text testId) {
156
+        if (!testId.hasValue()) return;
157
+        UiUtils.runOnPreDrawOnce(this.toolbar, () -> {
158
+            ActionMenuView buttonsLayout = ViewUtils.findChildByClass(toolbar, ActionMenuView.class);
159
+            List<TextView> buttons = ViewUtils.findChildrenByClass(buttonsLayout, TextView.class);
160
+            for (TextView view : buttons) {
161
+                if (button.title.hasValue() && button.title.get().equals(view.getText())) {
162
+                    view.setTag(testId.get());
163
+                } else if (button.icon.hasValue() && ArrayUtils.contains(view.getCompoundDrawables(), icon)) {
164
+                    view.setTag(testId.get());
165
+                }
166
+            }
167
+        });
168
+    }
169
+
170
+    @NonNull
171
+    private ArrayList<View> findActualTextViewInMenuByText() {
172
+        ArrayList<View> outViews = new ArrayList<>();
173
+        this.toolbar.findViewsWithText(outViews, button.title.get(), View.FIND_VIEWS_WITH_TEXT);
174
+        return outViews;
175
+    }
144
 }
176
 }

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

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.Nullable;
6
 import android.support.annotation.Nullable;
7
+import android.support.annotation.VisibleForTesting;
7
 import android.support.design.widget.AppBarLayout;
8
 import android.support.design.widget.AppBarLayout;
8
 import android.support.v4.view.ViewPager;
9
 import android.support.v4.view.ViewPager;
9
 import android.support.v7.widget.Toolbar;
10
 import android.support.v7.widget.Toolbar;
16
 import com.reactnativenavigation.anim.TopBarAnimator;
17
 import com.reactnativenavigation.anim.TopBarAnimator;
17
 import com.reactnativenavigation.anim.TopBarCollapseBehavior;
18
 import com.reactnativenavigation.anim.TopBarCollapseBehavior;
18
 import com.reactnativenavigation.interfaces.ScrollEventListener;
19
 import com.reactnativenavigation.interfaces.ScrollEventListener;
19
-import com.reactnativenavigation.parse.Button;
20
-import com.reactnativenavigation.parse.Color;
21
-import com.reactnativenavigation.parse.Fraction;
22
-import com.reactnativenavigation.parse.Number;
23
-import com.reactnativenavigation.parse.Options;
20
+import com.reactnativenavigation.parse.params.Button;
21
+import com.reactnativenavigation.parse.params.Color;
22
+import com.reactnativenavigation.parse.params.Fraction;
23
+import com.reactnativenavigation.parse.params.Number;
24
+import com.reactnativenavigation.parse.params.Bool;
24
 
25
 
25
 import java.util.ArrayList;
26
 import java.util.ArrayList;
26
 
27
 
28
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
29
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
30
+
27
 @SuppressLint("ViewConstructor")
31
 @SuppressLint("ViewConstructor")
28
 public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAwareView {
32
 public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAwareView {
29
     private final Toolbar titleBar;
33
     private final Toolbar titleBar;
30
     private TitleBarButton.OnClickListener onClickListener;
34
     private TitleBarButton.OnClickListener onClickListener;
31
     private final TopBarCollapseBehavior collapsingBehavior;
35
     private final TopBarCollapseBehavior collapsingBehavior;
32
-    private final TopBarAnimator animator;
36
+    private TopBarAnimator animator;
33
     private TopTabs topTabs;
37
     private TopTabs topTabs;
38
+    private StackLayout parentView;
34
 
39
 
35
-    public TopBar(final Context context, TitleBarButton.OnClickListener onClickListener) {
40
+    public TopBar(final Context context, TitleBarButton.OnClickListener onClickListener, StackLayout parentView) {
36
         super(context);
41
         super(context);
37
         this.onClickListener = onClickListener;
42
         this.onClickListener = onClickListener;
38
         collapsingBehavior = new TopBarCollapseBehavior(this);
43
         collapsingBehavior = new TopBarCollapseBehavior(this);
40
         titleBar.getMenu();
45
         titleBar.getMenu();
41
         topTabs = new TopTabs(getContext());
46
         topTabs = new TopTabs(getContext());
42
         this.animator = new TopBarAnimator(this);
47
         this.animator = new TopBarAnimator(this);
48
+        this.parentView = parentView;
43
         addView(titleBar);
49
         addView(titleBar);
44
     }
50
     }
45
 
51
 
51
         return titleBar.getTitle() != null ? titleBar.getTitle().toString() : "";
57
         return titleBar.getTitle() != null ? titleBar.getTitle().toString() : "";
52
     }
58
     }
53
 
59
 
60
+    public void setTestId(String testId) {
61
+        setTag(testId);
62
+    }
63
+
54
     public void setTitleTextColor(Color color) {
64
     public void setTitleTextColor(Color color) {
55
         if (color.hasValue()) titleBar.setTitleTextColor(color.get());
65
         if (color.hasValue()) titleBar.setTitleTextColor(color.get());
56
     }
66
     }
81
         topTabs.applyTopTabsFontSize(fontSize);
91
         topTabs.applyTopTabsFontSize(fontSize);
82
     }
92
     }
83
 
93
 
94
+    public void setTopTabsVisible(boolean visible) {
95
+        topTabs.setVisibility(this, visible);
96
+    }
97
+
84
     public void setButtons(ArrayList<Button> leftButtons, ArrayList<Button> rightButtons) {
98
     public void setButtons(ArrayList<Button> leftButtons, ArrayList<Button> rightButtons) {
85
         setLeftButtons(leftButtons);
99
         setLeftButtons(leftButtons);
86
         setRightButtons(rightButtons);
100
         setRightButtons(rightButtons);
147
         return titleBar;
161
         return titleBar;
148
     }
162
     }
149
 
163
 
150
-    public void setupTopTabsWithViewPager(ViewPager viewPager) {
151
-        initTopTabs();
152
-        topTabs.setupWithViewPager(viewPager);
153
-    }
154
-
155
-    private void initTopTabs() {
164
+    public void initTopTabs(ViewPager viewPager) {
156
         topTabs = new TopTabs(getContext());
165
         topTabs = new TopTabs(getContext());
166
+        topTabs.init(viewPager);
157
         addView(topTabs);
167
         addView(topTabs);
158
     }
168
     }
159
 
169
 
165
         collapsingBehavior.disableCollapse();
175
         collapsingBehavior.disableCollapse();
166
     }
176
     }
167
 
177
 
168
-    public void show(Options.BooleanOptions animated) {
178
+    public void show(Bool animated) {
169
         if (getVisibility() == View.VISIBLE) {
179
         if (getVisibility() == View.VISIBLE) {
170
             return;
180
             return;
171
         }
181
         }
172
-        if (animated == Options.BooleanOptions.True) {
182
+        if (animated.isTrueOrUndefined()) {
173
             animator.show();
183
             animator.show();
174
-        } else {
184
+        } else if (!animator.isRunning()) {
175
             setVisibility(View.VISIBLE);
185
             setVisibility(View.VISIBLE);
176
         }
186
         }
177
     }
187
     }
178
 
188
 
179
-    public void hide(Options.BooleanOptions animated) {
189
+    public void hide(Bool animated) {
180
         if (getVisibility() == View.GONE) {
190
         if (getVisibility() == View.GONE) {
181
             return;
191
             return;
182
         }
192
         }
183
-        if (animated == Options.BooleanOptions.True) {
193
+        if (animated.isTrueOrUndefined()) {
184
             animator.hide();
194
             animator.hide();
185
-        } else {
195
+        } else if (!animator.isRunning()){
186
             setVisibility(View.GONE);
196
             setVisibility(View.GONE);
187
         }
197
         }
188
     }
198
     }
189
 
199
 
200
+    @Override
201
+    public void setVisibility(int visibility) {
202
+        super.setVisibility(visibility);
203
+        if (visibility == View.GONE) {
204
+            this.parentView.removeView(this);
205
+        } else if (visibility == View.VISIBLE && this.getParent() == null) {
206
+            this.parentView.addView(this, MATCH_PARENT, WRAP_CONTENT);
207
+        }
208
+    }
209
+
190
     public void clear() {
210
     public void clear() {
191
         titleBar.setTitle(null);
211
         titleBar.setTitle(null);
192
         titleBar.setNavigationIcon(null);
212
         titleBar.setNavigationIcon(null);
193
         titleBar.getMenu().clear();
213
         titleBar.getMenu().clear();
194
-        removeView(topTabs);
214
+    }
215
+
216
+    public void clearTopTabs() {
217
+        topTabs.clear(this);
218
+    }
219
+
220
+    @VisibleForTesting()
221
+    public TopTabs getTopTabs() {
222
+        return topTabs;
223
+    }
224
+
225
+    @VisibleForTesting
226
+    public void setAnimator(TopBarAnimator animator) {
227
+        this.animator = animator;
195
     }
228
     }
196
 }
229
 }

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


Some files were not shown because too many files changed in this diff