Browse Source

merged branch v2.0, master is the next release

Daniel Zlotin 8 years ago
parent
commit
f99181df3b
100 changed files with 5251 additions and 3646 deletions
  1. 529
    0
      .eslintrc
  2. 15
    0
      .watchmanconfig
  3. 4
    0
      CHANGELOG.md
  4. 82
    3
      README.md
  5. 16
    1
      android/app/build.gradle
  6. 1
    5
      android/app/src/main/AndroidManifest.xml
  7. 96
    0
      android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java
  8. 0
    470
      android/app/src/main/java/com/reactnativenavigation/activities/BaseReactActivity.java
  9. 0
    309
      android/app/src/main/java/com/reactnativenavigation/activities/BottomTabActivity.java
  10. 0
    54
      android/app/src/main/java/com/reactnativenavigation/activities/RootActivity.java
  11. 0
    125
      android/app/src/main/java/com/reactnativenavigation/activities/SingleScreenActivity.java
  12. 0
    105
      android/app/src/main/java/com/reactnativenavigation/activities/TabActivity.java
  13. 0
    154
      android/app/src/main/java/com/reactnativenavigation/adapters/ViewPagerAdapter.java
  14. 120
    0
      android/app/src/main/java/com/reactnativenavigation/animation/VisibilityAnimator.java
  15. 68
    0
      android/app/src/main/java/com/reactnativenavigation/bridge/BundleConverter.java
  16. 41
    0
      android/app/src/main/java/com/reactnativenavigation/bridge/NavigationReactEventEmitter.java
  17. 144
    0
      android/app/src/main/java/com/reactnativenavigation/bridge/NavigationReactModule.java
  18. 3
    4
      android/app/src/main/java/com/reactnativenavigation/bridge/NavigationReactPackage.java
  19. 115
    0
      android/app/src/main/java/com/reactnativenavigation/controllers/Modal.java
  20. 81
    34
      android/app/src/main/java/com/reactnativenavigation/controllers/ModalController.java
  21. 215
    0
      android/app/src/main/java/com/reactnativenavigation/controllers/NavigationActivity.java
  22. 241
    0
      android/app/src/main/java/com/reactnativenavigation/controllers/NavigationCommandsHandler.java
  23. 57
    0
      android/app/src/main/java/com/reactnativenavigation/controllers/SplashActivity.java
  24. 0
    204
      android/app/src/main/java/com/reactnativenavigation/core/RctManager.java
  25. 0
    80
      android/app/src/main/java/com/reactnativenavigation/core/objects/Button.java
  26. 0
    23
      android/app/src/main/java/com/reactnativenavigation/core/objects/Drawer.java
  27. 0
    35
      android/app/src/main/java/com/reactnativenavigation/core/objects/JsonObject.java
  28. 0
    144
      android/app/src/main/java/com/reactnativenavigation/core/objects/Screen.java
  29. 285
    0
      android/app/src/main/java/com/reactnativenavigation/layouts/BottomTabsLayout.java
  30. 26
    0
      android/app/src/main/java/com/reactnativenavigation/layouts/Layout.java
  31. 36
    0
      android/app/src/main/java/com/reactnativenavigation/layouts/LayoutFactory.java
  32. 16
    0
      android/app/src/main/java/com/reactnativenavigation/layouts/ScreenStackContainer.java
  33. 168
    0
      android/app/src/main/java/com/reactnativenavigation/layouts/SingleScreenLayout.java
  34. 0
    109
      android/app/src/main/java/com/reactnativenavigation/modal/RnnModal.java
  35. 0
    346
      android/app/src/main/java/com/reactnativenavigation/modules/RctActivityModule.java
  36. 14
    0
      android/app/src/main/java/com/reactnativenavigation/params/ActivityParams.java
  37. 13
    0
      android/app/src/main/java/com/reactnativenavigation/params/AppStyle.java
  38. 27
    0
      android/app/src/main/java/com/reactnativenavigation/params/NavigationParams.java
  39. 44
    0
      android/app/src/main/java/com/reactnativenavigation/params/ScreenParams.java
  40. 7
    0
      android/app/src/main/java/com/reactnativenavigation/params/SideMenuParams.java
  41. 66
    0
      android/app/src/main/java/com/reactnativenavigation/params/StyleParams.java
  42. 33
    0
      android/app/src/main/java/com/reactnativenavigation/params/TitleBarButtonParams.java
  43. 23
    0
      android/app/src/main/java/com/reactnativenavigation/params/TitleBarLeftButtonParams.java
  44. 7
    0
      android/app/src/main/java/com/reactnativenavigation/params/TopTabParams.java
  45. 30
    0
      android/app/src/main/java/com/reactnativenavigation/params/parsers/ActivityParamsParser.java
  46. 23
    0
      android/app/src/main/java/com/reactnativenavigation/params/parsers/Parser.java
  47. 111
    0
      android/app/src/main/java/com/reactnativenavigation/params/parsers/ScreenParamsParser.java
  48. 17
    0
      android/app/src/main/java/com/reactnativenavigation/params/parsers/SideMenuParamsParser.java
  49. 159
    0
      android/app/src/main/java/com/reactnativenavigation/params/parsers/StyleParamsParser.java
  50. 59
    0
      android/app/src/main/java/com/reactnativenavigation/params/parsers/TitleBarButtonParamsParser.java
  51. 30
    0
      android/app/src/main/java/com/reactnativenavigation/params/parsers/TitleBarLeftButtonParamsParser.java
  52. 34
    0
      android/app/src/main/java/com/reactnativenavigation/params/parsers/TopTabParamsParser.java
  53. 16
    0
      android/app/src/main/java/com/reactnativenavigation/react/ImageLoader.java
  54. 37
    0
      android/app/src/main/java/com/reactnativenavigation/react/JsDevImageLoader.java
  55. 48
    0
      android/app/src/main/java/com/reactnativenavigation/react/JsDevReloadHandler.java
  56. 69
    0
      android/app/src/main/java/com/reactnativenavigation/react/JsDevReloadListenerReplacer.java
  57. 143
    0
      android/app/src/main/java/com/reactnativenavigation/react/NavigationReactGateway.java
  58. 36
    0
      android/app/src/main/java/com/reactnativenavigation/react/ReactGateway.java
  59. 26
    0
      android/app/src/main/java/com/reactnativenavigation/react/ReactViewHacks.java
  60. 26
    0
      android/app/src/main/java/com/reactnativenavigation/react/RedboxPermission.java
  61. 4
    2
      android/app/src/main/java/com/reactnativenavigation/react/ResourceDrawableIdHelper.java
  62. 60
    0
      android/app/src/main/java/com/reactnativenavigation/screens/ContentViewPagerAdapter.java
  63. 135
    0
      android/app/src/main/java/com/reactnativenavigation/screens/FragmentScreen.java
  64. 188
    0
      android/app/src/main/java/com/reactnativenavigation/screens/Screen.java
  65. 99
    0
      android/app/src/main/java/com/reactnativenavigation/screens/ScreenAnimator.java
  66. 20
    0
      android/app/src/main/java/com/reactnativenavigation/screens/ScreenFactory.java
  67. 256
    0
      android/app/src/main/java/com/reactnativenavigation/screens/ScreenStack.java
  68. 49
    0
      android/app/src/main/java/com/reactnativenavigation/screens/SingleScreen.java
  69. 95
    0
      android/app/src/main/java/com/reactnativenavigation/screens/ViewPagerScreen.java
  70. 0
    121
      android/app/src/main/java/com/reactnativenavigation/utils/BridgeUtils.java
  71. 0
    14
      android/app/src/main/java/com/reactnativenavigation/utils/CollectionUtils.java
  72. 0
    34
      android/app/src/main/java/com/reactnativenavigation/utils/ContextProvider.java
  73. 0
    76
      android/app/src/main/java/com/reactnativenavigation/utils/IconUtils.java
  74. 0
    28
      android/app/src/main/java/com/reactnativenavigation/utils/ImageUtils.java
  75. 19
    0
      android/app/src/main/java/com/reactnativenavigation/utils/IntentUtils.java
  76. 79
    0
      android/app/src/main/java/com/reactnativenavigation/utils/KeyboardVisibilityDetector.java
  77. 0
    20
      android/app/src/main/java/com/reactnativenavigation/utils/RefUtils.java
  78. 10
    27
      android/app/src/main/java/com/reactnativenavigation/utils/ReflectionUtils.java
  79. 0
    13
      android/app/src/main/java/com/reactnativenavigation/utils/SdkSupports.java
  80. 0
    50
      android/app/src/main/java/com/reactnativenavigation/utils/StyleHelper.java
  81. 5
    0
      android/app/src/main/java/com/reactnativenavigation/utils/Task.java
  82. 53
    0
      android/app/src/main/java/com/reactnativenavigation/utils/ViewUtils.java
  83. 0
    132
      android/app/src/main/java/com/reactnativenavigation/views/BottomNavigation.java
  84. 81
    0
      android/app/src/main/java/com/reactnativenavigation/views/BottomTabs.java
  85. 73
    0
      android/app/src/main/java/com/reactnativenavigation/views/ContentView.java
  86. 85
    0
      android/app/src/main/java/com/reactnativenavigation/views/LeftButton.java
  87. 7
    0
      android/app/src/main/java/com/reactnativenavigation/views/LeftButtonOnClickListener.java
  88. 0
    195
      android/app/src/main/java/com/reactnativenavigation/views/RctView.java
  89. 0
    34
      android/app/src/main/java/com/reactnativenavigation/views/RnnReactRootView.java
  90. 0
    77
      android/app/src/main/java/com/reactnativenavigation/views/RnnTabLayout.java
  91. 0
    439
      android/app/src/main/java/com/reactnativenavigation/views/RnnToolBar.java
  92. 0
    179
      android/app/src/main/java/com/reactnativenavigation/views/ScreenStack.java
  93. 53
    0
      android/app/src/main/java/com/reactnativenavigation/views/ScrollDirectionListener.java
  94. 74
    0
      android/app/src/main/java/com/reactnativenavigation/views/SideMenu.java
  95. 130
    0
      android/app/src/main/java/com/reactnativenavigation/views/TitleBar.java
  96. 98
    0
      android/app/src/main/java/com/reactnativenavigation/views/TitleBarButton.java
  97. 7
    0
      android/app/src/main/java/com/reactnativenavigation/views/TitleBarMenuButton.java
  98. 74
    0
      android/app/src/main/java/com/reactnativenavigation/views/TopBar.java
  99. 40
    0
      android/app/src/main/java/com/reactnativenavigation/views/TopTabs.java
  100. 0
    0
      android/app/src/main/res/layout/bottom_tab_activity.xml

+ 529
- 0
.eslintrc View File

@@ -0,0 +1,529 @@
1
+{
2
+  "parser": "babel-eslint",
3
+  "env": {
4
+    "es6": true,
5
+    "node": true
6
+  },
7
+  "parserOptions": {
8
+    "ecmaVersion": 7,
9
+    "sourceType": "module",
10
+    "ecmaFeatures": {
11
+      "impliedStrict": true,
12
+      "jsx": true,
13
+      "experimentalObjectRestSpread": true
14
+    }
15
+  },
16
+  "plugins": [
17
+    "react",
18
+    "react-native",
19
+    "babel"
20
+  ],
21
+  "rules": {
22
+    /*
23
+     *                          possible errors
24
+     */
25
+    "comma-dangle": "error",
26
+    "no-cond-assign": [
27
+      "error",
28
+      "except-parens"
29
+    ],
30
+    "no-console": "warn",
31
+    "no-constant-condition": "error",
32
+    "no-control-regex": "error",
33
+    "no-debugger": "warn",
34
+    "no-dupe-args": "error",
35
+    "no-dupe-keys": "error",
36
+    "no-duplicate-case": "error",
37
+    "no-empty": "error",
38
+    "no-empty-character-class": "error",
39
+    "no-ex-assign": "error",
40
+    "no-extra-boolean-cast": "error",
41
+    "no-extra-parens": 0,
42
+    "no-extra-semi": "error",
43
+    "no-func-assign": "error",
44
+    "no-inner-declarations": "error",
45
+    "no-invalid-regexp": "error",
46
+    "no-irregular-whitespace": "error",
47
+    "no-negated-in-lhs": "error",
48
+    "no-obj-calls": "error",
49
+    "no-regex-spaces": "error",
50
+    "no-sparse-arrays": "error",
51
+    "no-unexpected-multiline": "error",
52
+    "no-unreachable": "error",
53
+    "use-isnan": "error",
54
+    "valid-jsdoc": 0,
55
+    "valid-typeof": "error",
56
+    /*
57
+     *                          best practices
58
+     */
59
+    "accessor-pairs": "warn",
60
+    "array-callback-return": 0,
61
+    "block-scoped-var": "error",
62
+    "complexity": 0,
63
+    "consistent-return": "error",
64
+    "curly": [
65
+      "error",
66
+      "all"
67
+    ],
68
+    "default-case": "error",
69
+    "dot-location": [
70
+      "error",
71
+      "property"
72
+    ],
73
+    "dot-notation": "error",
74
+    "eqeqeq": "error",
75
+    "guard-for-in": "error",
76
+    "no-alert": "error",
77
+    "no-caller": "error",
78
+    "no-case-declarations": "error",
79
+    "no-div-regex": 0,
80
+    "no-else-return": 0,
81
+    "no-empty-function": "error",
82
+    "no-empty-pattern": "error",
83
+    "no-eq-null": "error",
84
+    "no-eval": "error",
85
+    "no-extend-native": "error",
86
+    "no-extra-bind": "error",
87
+    "no-extra-label": "error",
88
+    "no-fallthrough": "error",
89
+    "no-floating-decimal": "error",
90
+    "no-implicit-coercion": "error",
91
+    "no-implicit-globals": 0,
92
+    "no-implied-eval": "error",
93
+    "no-invalid-this": 0,
94
+    "no-iterator": "error",
95
+    "no-labels": "error",
96
+    "no-lone-blocks": "error",
97
+    "no-loop-func": "error",
98
+    "no-magic-numbers": 0,
99
+    "no-multi-spaces": "error",
100
+    "no-multi-str": 0,
101
+    "no-native-reassign": "error",
102
+    "no-new": "error",
103
+    "no-new-func": "error",
104
+    "no-new-wrappers": "error",
105
+    "no-octal": "error",
106
+    "no-octal-escape": "error",
107
+    "no-param-reassign": "error",
108
+    "no-proto": "error",
109
+    "no-redeclare": 0,
110
+    "no-return-assign": "error",
111
+    "no-script-url": "error",
112
+    "no-self-assign": "error",
113
+    "no-self-compare": "error",
114
+    "no-sequences": "error",
115
+    "no-throw-literal": "error",
116
+    "no-unmodified-loop-condition": 0,
117
+    "no-unused-expressions": "error",
118
+    "no-unused-labels": 0,
119
+    "no-useless-call": "error",
120
+    "no-useless-concat": "error",
121
+    "no-void": "error",
122
+    "no-warning-comments": 0,
123
+    "no-with": "error",
124
+    "radix": 0,
125
+    "vars-on-top": 0,
126
+    "wrap-iife": "error",
127
+    "yoda": 0,
128
+    "strict": [
129
+      "error",
130
+      "never"
131
+    ],
132
+    /*
133
+     *                           variables
134
+     */
135
+    "init-declarations": 0,
136
+    "no-catch-shadow": "error",
137
+    "no-delete-var": "error",
138
+    "no-label-var": "error",
139
+    "no-restricted-globals": "error",
140
+    "no-shadow": "error",
141
+    "no-shadow-restricted-names": "error",
142
+    "no-undef": 0,
143
+    "no-undef-init": "error",
144
+    "no-undefined": 0,
145
+    "no-unused-vars": 0,
146
+    "no-use-before-define": 0,
147
+    /*
148
+     *                          Node.js
149
+     */
150
+    "callback-return": 0,
151
+    "global-require": 0,
152
+    "handle-callback-err": 0,
153
+    "no-mixed-requires": "error",
154
+    "no-new-require": "error",
155
+    "no-path-concat": "error",
156
+    "no-process-env": 0,
157
+    "no-process-exit": "error",
158
+    "no-restricted-modules": 0,
159
+    "no-sync": 0,
160
+    /*
161
+     *                          style
162
+     */
163
+    "array-bracket-spacing": [
164
+      "error",
165
+      "never"
166
+    ],
167
+    "block-spacing": "error",
168
+    "brace-style": [
169
+      "error",
170
+      "1tbs"
171
+    ],
172
+    "camelcase": [
173
+      "error",
174
+      {
175
+        "properties": "never"
176
+      }
177
+    ],
178
+    "comma-spacing": [
179
+      "error",
180
+      {
181
+        "before": false,
182
+        "after": true
183
+      }
184
+    ],
185
+    "comma-style": [
186
+      "error",
187
+      "last"
188
+    ],
189
+    "computed-property-spacing": [
190
+      "error",
191
+      "never"
192
+    ],
193
+    "consistent-this": [
194
+      "error",
195
+      "self"
196
+    ],
197
+    "eol-last": [
198
+      "error",
199
+      "unix"
200
+    ],
201
+    "func-names": 0,
202
+    "func-style": 0,
203
+    "id-blacklist": 0,
204
+    "id-length": 0,
205
+    "id-match": 0,
206
+    "indent": [
207
+      "error",
208
+      2,
209
+      {
210
+        "SwitchCase": 1
211
+      }
212
+    ],
213
+    "jsx-quotes": "error",
214
+    "key-spacing": [
215
+      "error",
216
+      {
217
+        "singleLine": {
218
+          "beforeColon": false,
219
+          "afterColon": true,
220
+          "mode": "strict"
221
+        },
222
+        "multiLine": {
223
+          "beforeColon": false,
224
+          "afterColon": true,
225
+          "mode": "strict"
226
+        }
227
+      }
228
+    ],
229
+    "keyword-spacing": [
230
+      "error",
231
+      {
232
+        "before": true,
233
+        "after": true
234
+      }
235
+    ],
236
+    "linebreak-style": [
237
+      "error",
238
+      "unix"
239
+    ],
240
+    "lines-around-comment": 0,
241
+    "max-depth": [
242
+      "error",
243
+      4
244
+    ],
245
+    "max-len": [
246
+      "warn",
247
+      150
248
+    ],
249
+    "max-nested-callbacks": [
250
+      "error",
251
+      4
252
+    ],
253
+    "max-params": [
254
+      "error",
255
+      6
256
+    ],
257
+    "max-statements": [
258
+      "error",
259
+      15,
260
+      {
261
+        "ignoreTopLevelFunctions": true
262
+      }
263
+    ],
264
+    "new-cap": [
265
+      "error",
266
+      {
267
+        "capIsNewExceptions": [
268
+          "Immutable"
269
+        ]
270
+      }
271
+    ],
272
+    "new-parens": "error",
273
+    "newline-after-var": 0,
274
+    "newline-before-return": 0,
275
+    "newline-per-chained-call": 0,
276
+    "no-array-constructor": "error",
277
+    "no-bitwise": 0,
278
+    "no-continue": 0,
279
+    "no-inline-comments": 0,
280
+    "no-lonely-if": "error",
281
+    "no-mixed-spaces-and-tabs": 0,
282
+    "no-multiple-empty-lines": [
283
+      "error",
284
+      {
285
+        "max": 1
286
+      }
287
+    ],
288
+    "no-negated-condition": 0,
289
+    "no-nested-ternary": "error",
290
+    "no-new-object": "error",
291
+    "no-plusplus": 0,
292
+    "no-restricted-syntax": 0,
293
+    "no-spaced-func": "error",
294
+    "no-ternary": 0,
295
+    "no-trailing-spaces": [
296
+      "error",
297
+      {
298
+        "skipBlankLines": true
299
+      }
300
+    ],
301
+    "no-underscore-dangle": 0,
302
+    "no-unneeded-ternary": "error",
303
+    "no-whitespace-before-property": "error",
304
+    "object-curly-spacing": [
305
+      "error",
306
+      "never"
307
+    ],
308
+    "one-var": 0,
309
+    "one-var-declaration-per-line": 0,
310
+    "operator-assignment": 0,
311
+    "operator-linebreak": [
312
+      "error",
313
+      "before"
314
+    ],
315
+    "padded-blocks": [
316
+      "error",
317
+      "never"
318
+    ],
319
+    "quote-props": [
320
+      "error",
321
+      "consistent-as-needed"
322
+    ],
323
+    "quotes": 0,
324
+    "require-jsdoc": 0,
325
+    "semi": [
326
+      "error",
327
+      "always"
328
+    ],
329
+    "semi-spacing": [
330
+      "error",
331
+      {
332
+        "before": false,
333
+        "after": true
334
+      }
335
+    ],
336
+    "sort-imports": 0,
337
+    "sort-vars": 0,
338
+    "space-before-blocks": [
339
+      "error",
340
+      "always"
341
+    ],
342
+    "space-before-function-paren": [
343
+      "error",
344
+      "never"
345
+    ],
346
+    "space-in-parens": [
347
+      "error",
348
+      "never"
349
+    ],
350
+    "space-infix-ops": "error",
351
+    "space-unary-ops": "error",
352
+    "spaced-comment": 0,
353
+    "wrap-regex": "error",
354
+    /*
355
+     *                             ECMAScript 6
356
+     */
357
+    "arrow-body-style": 0,
358
+    "arrow-parens": 0,
359
+    "arrow-spacing": [
360
+      "error",
361
+      {
362
+        "before": true,
363
+        "after": true
364
+      }
365
+    ],
366
+    "constructor-super": "error",
367
+    "generator-star-spacing": 0,
368
+    "no-class-assign": "error",
369
+    "no-confusing-arrow": "error",
370
+    "no-const-assign": "error",
371
+    "no-dupe-class-members": "error",
372
+    "no-new-symbol": "error",
373
+    "no-restricted-imports": 0,
374
+    "no-this-before-super": "error",
375
+    "no-useless-constructor": 0,
376
+    "no-var": "error",
377
+    "object-shorthand": 0,
378
+    "prefer-arrow-callback": 0,
379
+    "prefer-const": "error",
380
+    "prefer-reflect": 0,
381
+    "prefer-rest-params": "error",
382
+    "prefer-spread": "error",
383
+    "prefer-template": 0,
384
+    "require-yield": 0,
385
+    "template-curly-spacing": [
386
+      "error",
387
+      "never"
388
+    ],
389
+    "yield-star-spacing": [
390
+      "error",
391
+      "after"
392
+    ],
393
+    /*
394
+     *                              react plugin
395
+     */
396
+    "react/display-name": 0,
397
+    "react/forbid-prop-types": [
398
+      "error",
399
+      {
400
+        "forbid": [
401
+          "any"
402
+        ]
403
+      }
404
+    ],
405
+    "react/no-danger": "error",
406
+    "react/no-deprecated": "error",
407
+    "react/no-did-mount-set-state": [
408
+      "error",
409
+      "allow-in-func"
410
+    ],
411
+    "react/no-did-update-set-state": [
412
+      "error",
413
+      "allow-in-func"
414
+    ],
415
+    "react/no-direct-mutation-state": "error",
416
+    "react/no-is-mounted": "error",
417
+    "react/no-multi-comp": "error",
418
+    "react/no-set-state": 0,
419
+    "react/no-string-refs": 0,
420
+    "react/no-unknown-property": 0,
421
+    "react/prefer-es6-class": [
422
+      "error",
423
+      "always"
424
+    ],
425
+    "react/prefer-stateless-function": 0,
426
+    "react/prop-types": "error",
427
+    "react/react-in-jsx-scope": "error",
428
+    "react/require-extension": 0,
429
+    "react/self-closing-comp": "error",
430
+    "react/sort-comp": [
431
+      "error",
432
+      {
433
+        "order": [
434
+          "static-methods",
435
+          "lifecycle",
436
+          "/^on.+$/",
437
+          "/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/",
438
+          "everything-else",
439
+          "/^render.+$/",
440
+          "render"
441
+        ]
442
+      }
443
+    ],
444
+    "react/sort-prop-types": 0,
445
+    "react/wrap-multilines": "error",
446
+    "react/jsx-boolean-value": [
447
+      "error",
448
+      "always"
449
+    ],
450
+    "react/jsx-closing-bracket-location": "error",
451
+    "react/jsx-curly-spacing": [
452
+      "error",
453
+      "never"
454
+    ],
455
+    "react/jsx-equals-spacing": [
456
+      "error",
457
+      "never"
458
+    ],
459
+    "react/jsx-handler-names": [
460
+      "error",
461
+      {
462
+        "eventHandlerPropPrefix": "handle"
463
+      }
464
+    ],
465
+    "react/jsx-indent-props": [
466
+      0,
467
+      2
468
+    ],
469
+    "react/jsx-indent": [
470
+      "error",
471
+      2
472
+    ],
473
+    "react/jsx-key": "error",
474
+    "react/jsx-max-props-per-line": [
475
+      "error",
476
+      {
477
+        "maximum": 2
478
+      }
479
+    ],
480
+    "react/jsx-no-bind": 0,
481
+    "react/jsx-no-duplicate-props": [
482
+      "error",
483
+      {
484
+        "ignoreCase": true
485
+      }
486
+    ],
487
+    "react/jsx-no-literals": "error",
488
+    "react/jsx-no-undef": "error",
489
+    "react/jsx-pascal-case": "error",
490
+    "react/jsx-sort-props": 0,
491
+    "react/jsx-space-before-closing": 0,
492
+    "react/jsx-uses-react": "error",
493
+    "react/jsx-uses-vars": "error",
494
+    /*
495
+     *                              react-native plugin
496
+     */
497
+    "react-native/no-unused-styles": "error",
498
+    "react-native/split-platform-components": "error",
499
+    /*
500
+     *                              babel plugin
501
+     */
502
+    "babel/generator-star-spacing": [
503
+      "error",
504
+      "after"
505
+    ],
506
+    "babel/new-cap": [
507
+      "error",
508
+      {
509
+        "capIsNewExceptions": [
510
+          "Immutable"
511
+        ]
512
+      }
513
+    ],
514
+    "babel/array-bracket-spacing": [
515
+      "error",
516
+      "never"
517
+    ],
518
+    "babel/object-curly-spacing": [
519
+      "error",
520
+      "never"
521
+    ],
522
+    "babel/object-shorthand": 0,
523
+    "babel/arrow-parens": [
524
+      "error",
525
+      "always"
526
+    ],
527
+    "babel/no-await-in-loop": 0
528
+  }
529
+}

+ 15
- 0
.watchmanconfig View File

@@ -0,0 +1,15 @@
1
+{
2
+  "ignore_dirs": [
3
+    ".git",
4
+    "node_modules",
5
+    ".gradle",
6
+    ".idea",
7
+    "gradle",
8
+    "build",
9
+    "example",
10
+    "example-redux",
11
+    "android/.gradle",
12
+    "android/gradle",
13
+    "android/app/build"
14
+  ]
15
+}

RELEASES.md → CHANGELOG.md View File

@@ -5,3 +5,7 @@
5 5
 * **Breaking change:** in order to handle nav button press events, you now need to register your event handler explicitly. Until now, you've just had to have `onNavigatorEvent(event)` method on your screen component. From now on, explicitly register your handler with `navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));`. A good place to do this is in your screen's constructor. See the example for an example.
6 6
 * Simplified API significantly. You now don't need to have your screen components extend `Screen`. You can leave them extending React's traditional `Component`. When you register your screen, instead of using `Navigation.registerScreen` directly, you can now use `Navigation.registerComponent`. This wrapper will wrap your regular component with a `Screen` automatically and pass the navigator instance through props. The benefit of this change is that your screen components now have *zero(!)* references to react-native-navigation. All of the changes required by this package are now purely concentrated in one place - your app bootstrap.
7 7
 * Added redux example and optional redux support
8
+
9
+### 2.0.0
10
+
11
+* Android native code redesign

+ 82
- 3
README.md View File

@@ -68,11 +68,90 @@ For example, this package replaces the native [NavigatorIOS](https://facebook.gi
68 68
 }
69 69
 ```
70 70
 
71
-3. Have your `MainActivity.java` extend `com.reactnativenavigation.activities.RootActivity`. 
72
-`RootActivity` is used as a proxy activity to start your actuall app.
71
+3. Your `MainActivity` should extend `com.reactnativenavigation.controllers.SplashActivity` instead of `ReactActivity`. If you have any `react-native` related methods in your `MainActivity` you can safely delete them.
73 72
 
74
-	The only method you might need to override is `getPackages()`, make sure you add `RnnPackage` as well.
73
+4. Create a custom Application class and update the `Application` element in `AndroidManifest.xml`
74
+	
75
+	```java
76
+	import com.reactnativenavigation.NavigationApplication;
77
+	
78
+	public class MyApplication extends NavigationApplication {
79
+	
80
+	}
81
+	```
82
+	
83
+	```xml
84
+	<application
85
+        android:name=".MyApplication"
86
+        ...
87
+        />
88
+	```
89
+5. Implement `isDebug` and `createAdditionalReactPackages` methods
90
+
91
+	```java
92
+	import com.reactnativenavigation.NavigationApplication;
93
+	
94
+	public class MyApplication extends NavigationApplication {
95
+ 
96
+    	@Override
97
+		public boolean isDebug() {
98
+			// Make sure you are using BuildConfig from your own application
99
+			return BuildConfig.DEBUG;
100
+		}
101
+
102
+	    @NonNull
103
+	    @Override
104
+	    public List<ReactPackage> createAdditionalReactPackages() {
105
+		    // Add the packages you require here.
106
+			// No need to add RnnPackage and MainReactPackage
107
+	        return null;
108
+	    }
109
+	}
110
+	```
111
+
112
+## Migrating to version 2.0
113
+Migrating your code base to version 2.0 will require a few changes to your native Java code. The actual navigation API has not changed so there will be no changes to your JS code base.
75 114
 
115
+* Your `MainActivity` should now extend `com.reactnativenavigation.controllers.SplashActivity`
116
+* Delete the `getPackages()` from `MainActivity`. Don't forget to delete unused imports after this step.
117
+* Create a custom Application class and update the `Application` element in `AndroidManifest.xml`
118
+	
119
+	```java
120
+	import com.reactnativenavigation.NavigationApplication;
121
+	
122
+	public class MyApplication extends NavigationApplication {
123
+	
124
+	}
125
+	```
126
+	
127
+	```xml
128
+	<application
129
+        android:name=".MyApplication"
130
+        ...
131
+        />
132
+	```
133
+* Implement `isDebug` and `createAdditionalReactPackages`
134
+
135
+	```java
136
+	import com.reactnativenavigation.NavigationApplication;
137
+	
138
+	public class MyApplication extends NavigationApplication {
139
+ 
140
+    	@Override
141
+		public boolean isDebug() {
142
+			// Make sure you are using BuildConfig from your own application
143
+			return BuildConfig.DEBUG;
144
+		}
145
+
146
+	    @NonNull
147
+	    @Override
148
+	    public List<ReactPackage> createAdditionalReactPackages() {
149
+		    // Add the packages you require here.
150
+			// No need to add RnnPackage and MainReactPackage
151
+	        return null;
152
+	    }
153
+	}
154
+	```
76 155
 
77 156
 ## Usage
78 157
 

+ 16
- 1
android/app/build.gradle View File

@@ -24,6 +24,9 @@ android {
24 24
             minifyEnabled false
25 25
         }
26 26
     }
27
+    lintOptions {
28
+        abortOnError false
29
+    }
27 30
 }
28 31
 
29 32
 repositories {
@@ -33,10 +36,22 @@ repositories {
33 36
     }
34 37
 }
35 38
 
39
+allprojects { p ->
40
+    p.tasks.whenTaskAdded { task ->
41
+        if (task.name.toLowerCase().contains('lint')) {
42
+            task.enabled = false;
43
+        }
44
+    }
45
+}
46
+
36 47
 dependencies {
37 48
     compile fileTree(dir: "libs", include: ["*.jar"])
38
-    compile "com.aurelhubert:ahbottomnavigation:1.2.3"
49
+    compile "com.aurelhubert:ahbottomnavigation:1.3.3"
39 50
     compile "com.android.support:appcompat-v7:23.0.1"
40 51
     compile 'com.android.support:design:23.1.1'
41 52
     compile "com.facebook.react:react-native:+"  // From node_modules
53
+    compile 'com.balysv.materialmenu:material-menu-toolbar:1.5.4'
54
+
55
+    testCompile "junit:junit:4.12"
56
+    testCompile "org.robolectric:robolectric:3.1.1"
42 57
 }

+ 1
- 5
android/app/src/main/AndroidManifest.xml View File

@@ -2,11 +2,7 @@
2 2
     package="com.reactnativenavigation">
3 3
 
4 4
     <application>
5
-        <activity android:name="com.reactnativenavigation.activities.TabActivity" />
6
-        <activity android:name="com.reactnativenavigation.activities.BottomTabActivity"
7
-                  android:label=""/>
8
-        <activity android:name="com.reactnativenavigation.activities.SingleScreenActivity"
9
-                  android:label=""/>
5
+        <activity android:name=".controllers.NavigationActivity" />
10 6
     </application>
11 7
 
12 8
 </manifest>

+ 96
- 0
android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java View File

@@ -0,0 +1,96 @@
1
+package com.reactnativenavigation;
2
+
3
+import android.app.Application;
4
+import android.os.Handler;
5
+import android.support.annotation.Nullable;
6
+
7
+import com.facebook.react.ReactPackage;
8
+import com.facebook.react.bridge.ReactContext;
9
+import com.facebook.react.bridge.WritableMap;
10
+import com.reactnativenavigation.react.NavigationReactGateway;
11
+import com.reactnativenavigation.react.ReactGateway;
12
+
13
+import java.util.List;
14
+
15
+public abstract class NavigationApplication extends Application {
16
+
17
+    public static NavigationApplication instance;
18
+
19
+    private ReactGateway reactGateway;
20
+    private Handler handler;
21
+
22
+    @Override
23
+    public void onCreate() {
24
+        super.onCreate();
25
+        instance = this;
26
+        reactGateway = new NavigationReactGateway();
27
+        handler = new Handler(getMainLooper());
28
+    }
29
+
30
+    public void startReactContext() {
31
+        reactGateway.startReactContextOnceInBackgroundAndExecuteJS();
32
+    }
33
+
34
+    public void runOnMainThread(Runnable runnable) {
35
+        handler.post(runnable);
36
+    }
37
+
38
+    public void runOnMainThread(Runnable runnable, long delay) {
39
+        handler.postDelayed(runnable, delay);
40
+    }
41
+
42
+    public ReactGateway getReactGateway() {
43
+        return reactGateway;
44
+    }
45
+
46
+    public boolean isReactContextInitialized() {
47
+        return reactGateway.isInitialized();
48
+    }
49
+
50
+    public void onReactInitialized(ReactContext reactContext) {
51
+        // nothing
52
+    }
53
+
54
+    public String getJsEntryFileName() {
55
+        return "index.android";
56
+    }
57
+
58
+    public String getBundleAssetName() {
59
+        return "index.android.bundle";
60
+    }
61
+
62
+    public abstract boolean isDebug();
63
+
64
+    @Nullable
65
+    public abstract List<ReactPackage> createAdditionalReactPackages();
66
+
67
+    //TODO move all these navigator junk elsewhere
68
+    public void sendNavigatorEvent(String eventId, String navigatorEventId) {
69
+        if (!isReactContextInitialized()) {
70
+            return;
71
+        }
72
+        reactGateway.getReactEventEmitter().sendNavigatorEvent(eventId, navigatorEventId);
73
+    }
74
+
75
+    public void sendNavigatorEvent(String eventId, String navigatorEventId, WritableMap data) {
76
+        if (!isReactContextInitialized()) {
77
+            return;
78
+        }
79
+        reactGateway.getReactEventEmitter().sendNavigatorEvent(eventId, navigatorEventId, data);
80
+    }
81
+
82
+    public void sendEvent(String eventId, String navigatorEventId) {
83
+        if (!isReactContextInitialized()) {
84
+            return;
85
+        }
86
+        reactGateway.getReactEventEmitter().sendEvent(eventId, navigatorEventId);
87
+    }
88
+
89
+    public void sendNavigatorEvent(String eventId, WritableMap arguments) {
90
+        if (!isReactContextInitialized()) {
91
+            return;
92
+        }
93
+        reactGateway.getReactEventEmitter().sendEvent(eventId, arguments);
94
+    }
95
+
96
+}

+ 0
- 470
android/app/src/main/java/com/reactnativenavigation/activities/BaseReactActivity.java View File

@@ -1,470 +0,0 @@
1
-package com.reactnativenavigation.activities;
2
-
3
-import android.content.Intent;
4
-import android.content.res.Configuration;
5
-import android.os.Build;
6
-import android.os.Bundle;
7
-import android.os.Handler;
8
-import android.provider.Settings;
9
-import android.support.annotation.CallSuper;
10
-import android.support.v4.widget.DrawerLayout;
11
-import android.support.v7.app.ActionBarDrawerToggle;
12
-import android.support.v7.app.AppCompatActivity;
13
-import android.util.Log;
14
-import android.view.KeyEvent;
15
-import android.view.Menu;
16
-import android.view.MenuItem;
17
-import android.widget.EditText;
18
-import android.widget.FrameLayout;
19
-import android.widget.Toast;
20
-
21
-import com.facebook.common.logging.FLog;
22
-import com.facebook.react.ReactInstanceManager;
23
-import com.facebook.react.ReactPackage;
24
-import com.facebook.react.ReactRootView;
25
-import com.facebook.react.bridge.Arguments;
26
-import com.facebook.react.bridge.ReadableMap;
27
-import com.facebook.react.bridge.WritableMap;
28
-import com.facebook.react.common.ReactConstants;
29
-import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
30
-import com.facebook.react.shell.MainReactPackage;
31
-import com.reactnativenavigation.BuildConfig;
32
-import com.reactnativenavigation.controllers.ModalController;
33
-import com.reactnativenavigation.core.RctManager;
34
-import com.reactnativenavigation.core.objects.Button;
35
-import com.reactnativenavigation.core.objects.Drawer;
36
-import com.reactnativenavigation.core.objects.Screen;
37
-import com.reactnativenavigation.modal.RnnModal;
38
-import com.reactnativenavigation.packages.RnnPackage;
39
-import com.reactnativenavigation.utils.ContextProvider;
40
-import com.reactnativenavigation.utils.StyleHelper;
41
-import com.reactnativenavigation.views.RnnToolBar;
42
-import com.reactnativenavigation.views.ScreenStack;
43
-
44
-import java.util.Arrays;
45
-import java.util.List;
46
-
47
-import javax.annotation.Nullable;
48
-
49
-public abstract class BaseReactActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
50
-
51
-    protected static final String KEY_ANIMATED = "animated";
52
-    protected static final String KEY_BADGE = "badge";
53
-    protected static final String KEY_HIDDEN = "hidden";
54
-    protected static final String KEY_SIDE = "side";
55
-    protected static final String KEY_TAB_INDEX = "tabIndex";
56
-    protected static final String KEY_TITLE = "title";
57
-    protected static final String KEY_TO = "to";
58
-    protected static final String KEY_NAVIGATOR_ID = "navigatorID";
59
-
60
-    private static final String TAG = "BaseReactActivity";
61
-    private static final String REDBOX_PERMISSION_MESSAGE =
62
-            "Overlay permissions needs to be granted in order for react native apps to run in dev mode";
63
-
64
-    @Nullable
65
-    protected ReactInstanceManager mReactInstanceManager;
66
-    private boolean mDoRefresh = false;
67
-    private Menu mMenu;
68
-    protected RnnToolBar mToolbar;
69
-    protected ActionBarDrawerToggle mDrawerToggle;
70
-    protected DrawerLayout mDrawerLayout;
71
-    protected ScreenStack mDrawerStack;
72
-
73
-    /**
74
-     * Returns the name of the bundle in assets. If this is null, and no file path is specified for
75
-     * the bundle, the app will only work with {@code getUseDeveloperSupport} enabled and will
76
-     * always try to load the JS bundle from the packager server.
77
-     * e.g. "index.android.bundle"
78
-     */
79
-    @Nullable
80
-    public String getBundleAssetName() {
81
-        return "index.android.bundle";
82
-    }
83
-
84
-    /**
85
-     * Returns a custom path of the bundle file. This is used in cases the bundle should be loaded
86
-     * from a custom path. By default it is loaded from Android assets, from a path specified
87
-     * by {getBundleAssetName}.
88
-     * e.g. "file://sdcard/myapp_cache/index.android.bundle"
89
-     */
90
-    @Nullable
91
-    public String getJSBundleFile() {
92
-        return null;
93
-    }
94
-
95
-    /**
96
-     * Returns the name of the main module. Determines the URL used to fetch the JS bundle
97
-     * from the packager server. It is only used when dev support is enabled.
98
-     * This is the first file to be executed once the {@link ReactInstanceManager} is created.
99
-     * e.g. "index.android"
100
-     */
101
-    public String getJSMainModuleName() {
102
-        return "index.android";
103
-    }
104
-
105
-    /**
106
-     * Returns the launchOptions which will be passed to the {@link ReactInstanceManager}
107
-     * when the application is started. By default, this will return null and an empty
108
-     * object will be passed to your top level component as its initial props.
109
-     * If your React Native application requires props set outside of JS, override
110
-     * this method to return the Android.os.Bundle of your desired initial props.
111
-     */
112
-    @Nullable
113
-    protected Bundle getLaunchOptions() {
114
-        return null;
115
-    }
116
-
117
-    /**
118
-     * Returns the name of the main component registered from JavaScript.
119
-     * This is used to schedule rendering of the component.
120
-     * e.g. "MoviesApp"
121
-     */
122
-    protected String getMainComponentName() {
123
-        return "";
124
-    }
125
-
126
-    /**
127
-     * Returns whether dev mode should be enabled. This enables e.g. the dev menu.
128
-     */
129
-    public boolean getUseDeveloperSupport() {
130
-        return BuildConfig.DEBUG;
131
-    }
132
-
133
-    /**
134
-     * Returns a list of {@link ReactPackage} used by the app.
135
-     * You'll most likely want to return at least the {@code MainReactPackage}.
136
-     * If your app uses additional views or modules besides the default ones,
137
-     * you'll want to include more packages here.
138
-     */
139
-    public List<ReactPackage> getPackages() {
140
-        return Arrays.asList(
141
-                new MainReactPackage(),
142
-                new RnnPackage()
143
-        );
144
-    }
145
-
146
-    /**
147
-     * A subclass may override this method if it needs to use a custom {@link ReactRootView}.
148
-     */
149
-    protected ReactRootView createRootView() {
150
-        return new ReactRootView(this);
151
-    }
152
-
153
-    @Override
154
-    protected void onCreate(Bundle savedInstanceState) {
155
-        super.onCreate(savedInstanceState);
156
-        ContextProvider.setActivityContext(this);
157
-        mReactInstanceManager = createReactInstanceManager();
158
-        handleOnCreate();
159
-
160
-    }
161
-
162
-    /**
163
-     * A subclass may override this method if it needs to use a custom instance.
164
-     */
165
-    protected ReactInstanceManager createReactInstanceManager() {
166
-        return getReactInstanceManager();
167
-    }
168
-
169
-    protected ReactInstanceManager getReactInstanceManager() {
170
-        RctManager rctManager = RctManager.getInstance();
171
-        if (!rctManager.isInitialized()) {
172
-            rctManager.init(this);
173
-        }
174
-        return rctManager.getReactInstanceManager();
175
-    }
176
-
177
-    @CallSuper
178
-    protected void handleOnCreate() {
179
-        permissionToShowRedboxIfNeeded();
180
-    }
181
-
182
-    private void permissionToShowRedboxIfNeeded() {
183
-        if (getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23 && !Settings.canDrawOverlays(this)) {
184
-            Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
185
-            startActivity(serviceIntent);
186
-            FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
187
-            Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
188
-        }
189
-    }
190
-
191
-    @Override
192
-    protected void onResume() {
193
-        super.onResume();
194
-        ContextProvider.setActivityContext(this);
195
-
196
-        if (mReactInstanceManager != null) {
197
-            mReactInstanceManager.onHostResume(this, this);
198
-        }
199
-    }
200
-
201
-    @Override
202
-    protected void onPause() {
203
-        super.onPause();
204
-
205
-        if (mReactInstanceManager != null) {
206
-            mReactInstanceManager.onHostPause();
207
-        }
208
-
209
-        ContextProvider.clearActivityContext();
210
-    }
211
-
212
-    @Override
213
-    protected void onDestroy() {
214
-        super.onDestroy();
215
-
216
-        // Destroy react instance manager only if there are no resumed react activities
217
-        BaseReactActivity activity = ContextProvider.getActivityContext();
218
-        if (mReactInstanceManager != null && (activity == null || activity.isFinishing())) {
219
-            Log.i(TAG, "Destroying ReactInstanceManager");
220
-            mReactInstanceManager.onHostDestroy();
221
-            RctManager.getInstance().onDestroy();
222
-        } else {
223
-            Log.d(TAG, "Not destroying ReactInstanceManager");
224
-        }
225
-    }
226
-
227
-    @CallSuper
228
-    public void push(Screen screen) {
229
-        StyleHelper.updateStyles(mToolbar, screen);
230
-        if (mToolbar != null) {
231
-            mToolbar.update(screen);
232
-
233
-            if (getCurrentNavigatorId().equals(screen.navigatorId) &&
234
-                    getScreenStackSize() >= 1) {
235
-                mToolbar.setNavUpButton(screen);
236
-            }
237
-        }
238
-    }
239
-
240
-    @CallSuper
241
-    public Screen pop(String navigatorId) {
242
-        if (mToolbar != null &&
243
-                getCurrentNavigatorId().equals(navigatorId) &&
244
-                getScreenStackSize() <= 2) {
245
-            mToolbar.setNavUpButton();
246
-        }
247
-
248
-        return null;
249
-    }
250
-
251
-    @CallSuper
252
-    public Screen popToRoot(String navigatorId) {
253
-        if (mToolbar != null) {
254
-            mToolbar.setNavUpButton();
255
-        }
256
-
257
-        return null;
258
-    }
259
-
260
-    @CallSuper
261
-    public Screen resetTo(Screen screen) {
262
-        StyleHelper.updateStyles(mToolbar, screen);
263
-        if (mToolbar != null) {
264
-            mToolbar.setNavUpButton();
265
-        }
266
-
267
-        return null;
268
-    }
269
-
270
-    protected abstract String getCurrentNavigatorId();
271
-
272
-    @CallSuper
273
-    public Screen getCurrentScreen() {
274
-        ModalController modalController = ModalController.getInstance();
275
-        if (modalController.isModalDisplayed()) {
276
-            RnnModal modal = modalController.get();
277
-            assert modal != null;
278
-            return modal.getCurrentScreen();
279
-        }
280
-
281
-        return null;
282
-    }
283
-
284
-    public abstract int getScreenStackSize();
285
-
286
-    @Override
287
-    public void onConfigurationChanged(Configuration newConfig) {
288
-        super.onConfigurationChanged(newConfig);
289
-        if (mDrawerToggle != null) {
290
-            mDrawerToggle.onConfigurationChanged(newConfig);
291
-        }
292
-    }
293
-
294
-    @Override
295
-    public boolean onCreateOptionsMenu(Menu menu) {
296
-        mMenu = menu;
297
-        Screen currentScreen = getCurrentScreen();
298
-        if (mToolbar != null && currentScreen != null && !isFinishing()) {
299
-            mToolbar.setupToolbarButtonsAsync(currentScreen);
300
-        }
301
-        return super.onCreateOptionsMenu(menu);
302
-    }
303
-
304
-    @Override
305
-    public boolean onOptionsItemSelected(MenuItem item) {
306
-        if (mDrawerToggle != null &&
307
-                getScreenStackSize() == 1 &&
308
-                mDrawerToggle.onOptionsItemSelected(item)) {
309
-            return true;
310
-        }
311
-
312
-        if (item.getItemId() == android.R.id.home) {
313
-            onBackPressed();
314
-        } else {
315
-            String eventId = Button.getButtonEventId(item);
316
-            assert eventId != null;
317
-
318
-            WritableMap params = Arguments.createMap();
319
-            RctManager.getInstance().sendEvent(eventId, getCurrentScreen(), params);
320
-        }
321
-        return super.onOptionsItemSelected(item);
322
-    }
323
-
324
-    @Override
325
-    public void onPostCreate(Bundle savedInstanceState) {
326
-        super.onPostCreate(savedInstanceState);
327
-        if (mDrawerToggle != null) {
328
-            mDrawerToggle.syncState();
329
-        }
330
-    }
331
-
332
-    public Menu getMenu() {
333
-        return mMenu;
334
-    }
335
-
336
-    @Override
337
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
338
-        if (mReactInstanceManager != null) {
339
-            mReactInstanceManager.onActivityResult(requestCode, resultCode, data);
340
-        }
341
-    }
342
-
343
-    @Override
344
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
345
-        if (mReactInstanceManager != null &&
346
-                mReactInstanceManager.getDevSupportManager().getDevSupportEnabled()) {
347
-            if (keyCode == KeyEvent.KEYCODE_MENU) {
348
-                mReactInstanceManager.showDevOptionsDialog();
349
-                return true;
350
-            }
351
-            if (keyCode == KeyEvent.KEYCODE_R && !(getCurrentFocus() instanceof EditText)) {
352
-                // Enable double-tap-R-to-reload
353
-                if (mDoRefresh) {
354
-                    mReactInstanceManager.getDevSupportManager().handleReloadJS();
355
-                    mDoRefresh = false;
356
-                } else {
357
-                    mDoRefresh = true;
358
-                    new Handler().postDelayed(
359
-                            new Runnable() {
360
-                                @Override
361
-                                public void run() {
362
-                                    mDoRefresh = false;
363
-                                }
364
-                            },
365
-                            200);
366
-                }
367
-            }
368
-        }
369
-        return super.onKeyUp(keyCode, event);
370
-    }
371
-
372
-    /**
373
-     * Called after bundle was reloaded. This is a good chance to clean up previously connected react views.
374
-     */
375
-    public void onJSBundleReloaded() {
376
-        removeAllReactViews();
377
-    }
378
-
379
-    protected abstract void removeAllReactViews();
380
-
381
-    @Override
382
-    public void onBackPressed() {
383
-        ModalController modalController = ModalController.getInstance();
384
-        if (modalController.isModalDisplayed()) {
385
-            modalController.dismissModal();
386
-            return;
387
-        }
388
-
389
-        if (getScreenStackSize() > 1) {
390
-            pop(getCurrentNavigatorId());
391
-        } else if (mReactInstanceManager != null) {
392
-            mReactInstanceManager.onBackPressed();
393
-        } else {
394
-            super.onBackPressed();
395
-        }
396
-    }
397
-
398
-    @Override
399
-    public void invokeDefaultOnBackPressed() {
400
-        super.onBackPressed();
401
-    }
402
-
403
-    protected void setupDrawer(Screen screen, Drawer drawer, int drawerFrameId, int drawerLayoutId) {
404
-        if (drawer == null || drawer.left == null) {
405
-            return;
406
-        }
407
-
408
-        mDrawerStack = new ScreenStack(this);
409
-        FrameLayout drawerFrame = (FrameLayout) findViewById(drawerFrameId);
410
-        drawerFrame.addView(mDrawerStack);
411
-        mDrawerStack.push(drawer.left);
412
-
413
-        mDrawerLayout = (DrawerLayout) findViewById(drawerLayoutId);
414
-        mDrawerToggle = mToolbar.setupDrawer(mDrawerLayout, drawer.left, screen);
415
-    }
416
-
417
-    public void setNavigationButtons(ReadableMap buttons) {
418
-        if (mToolbar == null) {
419
-            return;
420
-        }
421
-        getCurrentScreen().setButtons(buttons);
422
-        mToolbar.setupToolbarButtonsAsync(getCurrentScreen());
423
-    }
424
-
425
-    public void setNavigationTitle(ReadableMap title) {
426
-        if (mToolbar == null) {
427
-            return;
428
-        }
429
-
430
-        mToolbar.setTitle(title.getString(KEY_TITLE));
431
-    }
432
-
433
-    public void toggleNavigationBar(ReadableMap params) {
434
-        if (mToolbar == null) {
435
-            return;
436
-        }
437
-
438
-        boolean hide = params.getBoolean(KEY_HIDDEN);
439
-        boolean animated = params.getBoolean(KEY_ANIMATED);
440
-        if (hide) {
441
-            mToolbar.hideToolbar(animated);
442
-        } else {
443
-            mToolbar.showToolbar(animated);
444
-        }
445
-    }
446
-
447
-    public void toggleDrawer(ReadableMap params) {
448
-        if (mToolbar == null || mDrawerToggle == null) {
449
-            return;
450
-        }
451
-
452
-        boolean animated = params.getBoolean(KEY_ANIMATED);
453
-        String side = params.getString(KEY_SIDE);
454
-        String to = params.getString(KEY_TO);
455
-        switch (to) {
456
-            case "open":
457
-                mToolbar.showDrawer(animated);
458
-                break;
459
-            case "closed":
460
-                mToolbar.hideDrawer(animated);
461
-                break;
462
-            default:
463
-                mToolbar.toggleDrawer(animated);
464
-                break;
465
-        }
466
-    }
467
-
468
-    public void showFAB(ReadableMap params) {
469
-    }
470
-}

+ 0
- 309
android/app/src/main/java/com/reactnativenavigation/activities/BottomTabActivity.java View File

@@ -1,309 +0,0 @@
1
-package com.reactnativenavigation.activities;
2
-
3
-import android.graphics.Color;
4
-import android.graphics.drawable.Drawable;
5
-import android.os.AsyncTask;
6
-import android.os.Bundle;
7
-import android.support.design.widget.CoordinatorLayout;
8
-
9
-import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
10
-import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
11
-import com.facebook.react.bridge.ReadableMap;
12
-import com.reactnativenavigation.R;
13
-import com.reactnativenavigation.core.RctManager;
14
-import com.reactnativenavigation.core.objects.Drawer;
15
-import com.reactnativenavigation.core.objects.Screen;
16
-import com.reactnativenavigation.utils.StyleHelper;
17
-import com.reactnativenavigation.views.BottomNavigation;
18
-import com.reactnativenavigation.views.RnnToolBar;
19
-import com.reactnativenavigation.views.ScreenStack;
20
-
21
-import java.util.ArrayList;
22
-import java.util.HashMap;
23
-import java.util.Map;
24
-
25
-public class BottomTabActivity extends BaseReactActivity implements AHBottomNavigation.OnTabSelectedListener {
26
-    private static final String TAG = "BottomTabActivity";
27
-    public static final String DRAWER_PARAMS = "drawerParams";
28
-    public static final String EXTRA_SCREENS = "extraScreens";
29
-
30
-    private static final String TAB_STYLE_BUTTON_COLOR = "tabBarButtonColor";
31
-    private static final String TAB_STYLE_SELECTED_COLOR = "tabBarSelectedButtonColor";
32
-    private static final String TAB_STYLE_BAR_BG_COLOR = "tabBarBackgroundColor";
33
-    private static final String TAB_STYLE_INACTIVE_TITLES = "tabShowInactiveTitles";
34
-
35
-    private static int DEFAULT_TAB_BAR_BG_COLOR = 0xFFFFFFFF;
36
-    private static int DEFAULT_TAB_BUTTON_COLOR = Color.GRAY;
37
-    private static int DEFAULT_TAB_SELECTED_COLOR = 0xFF0000FF;
38
-    private static boolean DEFAULT_TAB_INACTIVE_TITLES = true;
39
-
40
-    private BottomNavigation mBottomNavigation;
41
-    private CoordinatorLayout mContentFrame;
42
-    private ArrayList<ScreenStack> mScreenStacks;
43
-    private int mCurrentStackPosition = -1;
44
-
45
-    @Override
46
-    protected void handleOnCreate() {
47
-        super.handleOnCreate();
48
-        mReactInstanceManager = RctManager.getInstance().getReactInstanceManager();
49
-
50
-        setContentView(R.layout.bottom_tab_activity);
51
-        mToolbar = (RnnToolBar) findViewById(R.id.toolbar);
52
-        mBottomNavigation = (BottomNavigation) findViewById(R.id.bottom_tab_bar);
53
-        mContentFrame = (CoordinatorLayout) findViewById(R.id.contentFrame);
54
-
55
-        final ArrayList<Screen> screens = (ArrayList<Screen>) getIntent().getSerializableExtra(EXTRA_SCREENS);
56
-        final Drawer drawer = (Drawer) getIntent().getSerializableExtra(DRAWER_PARAMS);
57
-        mBottomNavigation.setForceTint(true);
58
-        setupDrawer(screens.get(0), drawer, R.id.drawerFrame, R.id.drawerLayout);
59
-        setupTabs(getIntent().getExtras());
60
-        setupPages(screens);
61
-
62
-        // Setup Toolbar after it's measured since icon height is dependent on Toolbar height
63
-        mContentFrame.post(new Runnable() {
64
-            @Override
65
-            public void run() {
66
-                setupToolbar(screens);
67
-            }
68
-        });
69
-    }
70
-
71
-    private void setupPages(ArrayList<Screen> screens) {
72
-        new SetupTabsTask(this, mToolbar, screens).execute();
73
-    }
74
-
75
-    private void setupToolbar(ArrayList<Screen> screens) {
76
-        mToolbar.setScreens(screens);
77
-        Screen initialScreen = screens.get(0);
78
-        mToolbar.update(initialScreen);
79
-        StyleHelper.updateStyles(mToolbar, initialScreen);
80
-    }
81
-
82
-    @Override
83
-    protected void onResume() {
84
-        super.onResume();
85
-        if (mScreenStacks != null) {
86
-            StyleHelper.updateStyles(mToolbar, getCurrentScreen());
87
-        }
88
-    }
89
-
90
-    private void setupTabs(Bundle style) {
91
-        mBottomNavigation.setForceTitlesDisplay(style.getBoolean(TAB_STYLE_INACTIVE_TITLES, DEFAULT_TAB_INACTIVE_TITLES));
92
-        mBottomNavigation.setForceTint(true);
93
-        mBottomNavigation.setDefaultBackgroundColor(getColor(style, TAB_STYLE_BAR_BG_COLOR, DEFAULT_TAB_BAR_BG_COLOR));
94
-        mBottomNavigation.setInactiveColor(getColor(style, TAB_STYLE_BUTTON_COLOR, DEFAULT_TAB_BUTTON_COLOR));
95
-        mBottomNavigation.setAccentColor(getColor(style, TAB_STYLE_SELECTED_COLOR, DEFAULT_TAB_SELECTED_COLOR));
96
-    }
97
-
98
-    private static int getColor(Bundle bundle, String key, int defaultColor) {
99
-        if (bundle.containsKey(key)) {
100
-            return Color.parseColor(bundle.getString(key));
101
-        } else {
102
-            return defaultColor;
103
-        }
104
-    }
105
-
106
-    @Override
107
-    public void push(Screen screen) {
108
-        super.push(screen);
109
-        for (ScreenStack stack : mScreenStacks) {
110
-            if (stack.peek().navigatorId.equals(screen.navigatorId)) {
111
-                stack.push(screen);
112
-            }
113
-        }
114
-        StyleHelper.updateStyles(mToolbar, getCurrentScreen());
115
-
116
-        if (shouldToggleTabs(screen)) {
117
-            mBottomNavigation.toggleTabs(screen.bottomTabsHidden, false);
118
-        }
119
-    }
120
-
121
-    @Override
122
-    public Screen pop(String navigatorId) {
123
-        super.pop(navigatorId);
124
-        for (ScreenStack stack : mScreenStacks) {
125
-            if (stack.peek().navigatorId.equals(navigatorId)) {
126
-                Screen popped = stack.pop();
127
-                Screen currentScreen = getCurrentScreen();
128
-                StyleHelper.updateStyles(mToolbar, currentScreen);
129
-
130
-                if (shouldToggleTabs(currentScreen)) {
131
-                    mBottomNavigation.toggleTabs(currentScreen.bottomTabsHidden, false);
132
-                }
133
-
134
-                return popped;
135
-            }
136
-        }
137
-        return null;
138
-    }
139
-
140
-    @Override
141
-    public Screen popToRoot(String navigatorId) {
142
-        super.popToRoot(navigatorId);
143
-        for (ScreenStack stack : mScreenStacks) {
144
-            if (stack.peek().navigatorId.equals(navigatorId)) {
145
-                Screen popped = stack.popToRoot();
146
-                Screen currentScreen = getCurrentScreen();
147
-                StyleHelper.updateStyles(mToolbar, currentScreen);
148
-
149
-                if (shouldToggleTabs(currentScreen)) {
150
-                    mBottomNavigation.toggleTabs(currentScreen.bottomTabsHidden, false);
151
-                }
152
-
153
-                return popped;
154
-            }
155
-        }
156
-        return null;
157
-    }
158
-
159
-    @Override
160
-    public Screen getCurrentScreen() {
161
-        Screen currentScreen = super.getCurrentScreen();
162
-        if (currentScreen != null) {
163
-            return currentScreen;
164
-        }
165
-
166
-        return mScreenStacks != null ? mScreenStacks.get(mCurrentStackPosition).peek() : null;
167
-    }
168
-
169
-    public Screen resetTo(Screen screen) {
170
-        super.resetTo(screen);
171
-        StyleHelper.updateStyles(mToolbar, screen);
172
-        return mScreenStacks.get(mCurrentStackPosition).resetTo(screen);
173
-    }
174
-
175
-    @Override
176
-    protected String getCurrentNavigatorId() {
177
-        return mScreenStacks.get(mCurrentStackPosition).peek().navigatorId;
178
-    }
179
-
180
-    @Override
181
-    public int getScreenStackSize() {
182
-        return mScreenStacks.get(mCurrentStackPosition).getStackSize();
183
-    }
184
-
185
-    @Override
186
-    public void onTabSelected(int position, boolean wasSelected) {
187
-        if (wasSelected) {
188
-            return;
189
-        }
190
-
191
-        // Remove current ScreenStack
192
-        if (mCurrentStackPosition >= 0) {
193
-            mScreenStacks.get(mCurrentStackPosition).removeFromScreen(mContentFrame);
194
-        }
195
-
196
-        // Add new ScreenStack
197
-        mScreenStacks.get(position).addToScreen(mContentFrame);
198
-
199
-        mCurrentStackPosition = position;
200
-        StyleHelper.updateStyles(mToolbar, getCurrentScreen());
201
-
202
-        // Hide or show back button if needed
203
-        if (getScreenStackSize() > 1) {
204
-            mToolbar.setNavUpButton(getCurrentScreen());
205
-        } else {
206
-            mToolbar.setNavUpButton();
207
-        }
208
-    }
209
-
210
-    public void setTabBadge(ReadableMap params) {
211
-        // Badge comes across as int, but if it's 0 clear the notification
212
-        int badgeCount = params.getInt(KEY_BADGE);
213
-        String badge = (badgeCount > 0) ? Integer.toString(badgeCount) : "";
214
-
215
-        // Tab index is optional, so default to current tab
216
-        int tabIndex = mBottomNavigation.getCurrentItem();
217
-        if (params.hasKey(KEY_TAB_INDEX)) {
218
-            tabIndex = params.getInt(KEY_TAB_INDEX);
219
-        }
220
-
221
-        mBottomNavigation.setNotification(badge, tabIndex);
222
-    }
223
-
224
-    public void switchToTab(ReadableMap params) {
225
-        Integer tabIndex;
226
-        if (params.hasKey(KEY_TAB_INDEX)) {
227
-            tabIndex = params.getInt(KEY_TAB_INDEX);
228
-        } else {
229
-            final String navigatorId = params.getString(KEY_NAVIGATOR_ID);
230
-            tabIndex = findNavigatorTabIndex(navigatorId);
231
-        }
232
-        mBottomNavigation.setCurrentItem(tabIndex);
233
-    }
234
-
235
-    public void toggleTabs(ReadableMap params) {
236
-        boolean hide = params.getBoolean(KEY_HIDDEN);
237
-        boolean animated = params.getBoolean(KEY_ANIMATED);
238
-        mBottomNavigation.toggleTabs(hide, animated);
239
-    }
240
-
241
-    private boolean shouldToggleTabs(Screen newScreen) {
242
-        return mBottomNavigation.isShown() == newScreen.bottomTabsHidden;
243
-    }
244
-
245
-    public void onScrollChanged(int direction) {
246
-        mBottomNavigation.onScroll(direction);
247
-    }
248
-
249
-    private static class SetupTabsTask extends AsyncTask<Void, Void, Map<Screen, Drawable>> {
250
-        private BottomTabActivity mActivity;
251
-        private RnnToolBar mToolBar;
252
-        private ArrayList<Screen> mScreens;
253
-
254
-        public SetupTabsTask(BottomTabActivity context, RnnToolBar toolBar, ArrayList<Screen> screens) {
255
-            mActivity = context;
256
-            mToolBar = toolBar;
257
-            mScreens = screens;
258
-        }
259
-
260
-        @Override
261
-        protected Map<Screen, Drawable> doInBackground(Void... params) {
262
-            Map<Screen, Drawable> icons = new HashMap<>();
263
-            for (Screen screen : mScreens) {
264
-                if (screen.icon != null) {
265
-                    icons.put(screen, screen.getIcon(this.mActivity));
266
-                }
267
-            }
268
-            return icons;
269
-        }
270
-
271
-        @Override
272
-        protected void onPostExecute(Map<Screen, Drawable> icons) {
273
-            mActivity.setTabsWithIcons(mScreens, icons);
274
-            StyleHelper.updateStyles(mToolBar, mActivity.getCurrentScreen());
275
-        }
276
-    }
277
-
278
-    protected Integer findNavigatorTabIndex(String navigatorId) {
279
-        for (int i = 0; i < mScreenStacks.size(); i++) {
280
-            ScreenStack stack = mScreenStacks.get(i);
281
-            if (!stack.isEmpty() && stack.peek().navigatorId.equals(navigatorId)) {
282
-                return i;
283
-            }
284
-        }
285
-
286
-        return null;
287
-    }
288
-
289
-    private void setTabsWithIcons(ArrayList<Screen> screens, Map<Screen, Drawable> icons) {
290
-        mScreenStacks = new ArrayList<>();
291
-        for (int i = 0; i < screens.size(); i++) {
292
-            final Screen screen = screens.get(i);
293
-            ScreenStack stack = new ScreenStack(this);
294
-            stack.push(screen);
295
-            mScreenStacks.add(stack);
296
-            AHBottomNavigationItem item = new AHBottomNavigationItem(screen.label, icons.get(screen), Color.GRAY);
297
-            mBottomNavigation.addItem(item);
298
-            mBottomNavigation.setOnTabSelectedListener(this);
299
-        }
300
-        this.onTabSelected(0, false);
301
-    }
302
-
303
-    @Override
304
-    protected void removeAllReactViews() {
305
-        for (ScreenStack screenStack : mScreenStacks) {
306
-            screenStack.removeAllReactViews();
307
-        }
308
-    }
309
-}

+ 0
- 54
android/app/src/main/java/com/reactnativenavigation/activities/RootActivity.java View File

@@ -1,54 +0,0 @@
1
-package com.reactnativenavigation.activities;
2
-
3
-import android.view.ViewGroup;
4
-
5
-import com.facebook.react.ReactRootView;
6
-import com.reactnativenavigation.core.objects.Screen;
7
-
8
-/**
9
- * This activity is used to start the JS execution where we load our actual app/screens (index.android.js)
10
- * Triggering react context initialization execute global code before any {@link ReactRootView}
11
- * are displayed.
12
- * <p>Only your MainActivity or activities with {@code action.MAIN} and {@code category.LAUNCHER}
13
- * should extend this activity.
14
- * Created by guyc on 13/04/16.
15
- */
16
-public class RootActivity extends BaseReactActivity {
17
-
18
-    @Override
19
-    protected void handleOnCreate() {
20
-        super.handleOnCreate();
21
-        if (!getReactInstanceManager().hasStartedCreatingInitialContext()) {
22
-            // Trigger react context initialization, global javascript code will now execute
23
-            getReactInstanceManager().createReactContextInBackground();
24
-        }
25
-    }
26
-
27
-    @Override
28
-    protected void onPause() {
29
-        super.onPause();
30
-        finish();
31
-    }
32
-
33
-    // No need to implement stack interface since this activity is only used to start other
34
-    // activities such as TabActivity or SingleScreenActivity.
35
-    @Override
36
-    public Screen getCurrentScreen() {
37
-        return null;
38
-    }
39
-
40
-    @Override
41
-    public String getCurrentNavigatorId() {
42
-        return null;
43
-    }
44
-
45
-    @Override
46
-    public int getScreenStackSize() {
47
-        return 0;
48
-    }
49
-
50
-    @Override
51
-    public void removeAllReactViews() {
52
-
53
-    }
54
-}

+ 0
- 125
android/app/src/main/java/com/reactnativenavigation/activities/SingleScreenActivity.java View File

@@ -1,125 +0,0 @@
1
-package com.reactnativenavigation.activities;
2
-
3
-import android.support.design.widget.CoordinatorLayout;
4
-
5
-import com.facebook.react.bridge.ReadableMap;
6
-import com.reactnativenavigation.R;
7
-import com.reactnativenavigation.core.RctManager;
8
-import com.reactnativenavigation.core.objects.Drawer;
9
-import com.reactnativenavigation.core.objects.Screen;
10
-import com.reactnativenavigation.utils.StyleHelper;
11
-import com.reactnativenavigation.views.RnnToolBar;
12
-import com.reactnativenavigation.views.ScreenStack;
13
-
14
-public class SingleScreenActivity extends BaseReactActivity {
15
-
16
-    public static final String DRAWER_PARAMS = "drawerParams";
17
-    public static final String EXTRA_SCREEN = "extraScreen";
18
-
19
-    private ScreenStack mScreenStack;
20
-    private String mNavigatorId;
21
-
22
-    @Override
23
-    protected void handleOnCreate() {
24
-        super.handleOnCreate();
25
-        mReactInstanceManager = RctManager.getInstance().getReactInstanceManager();
26
-
27
-        setContentView(R.layout.single_screen_activity);
28
-        mToolbar = (RnnToolBar) findViewById(R.id.toolbar);
29
-
30
-        final Screen screen = (Screen) getIntent().getSerializableExtra(EXTRA_SCREEN);
31
-        final Drawer drawer = (Drawer) getIntent().getSerializableExtra(DRAWER_PARAMS);
32
-
33
-        mNavigatorId = screen.navigatorId;
34
-        setupToolbar(screen);
35
-        setupDrawer(screen, drawer, R.id.drawerFrame, R.id.drawerLayout);
36
-
37
-        mScreenStack = new ScreenStack(this);
38
-        CoordinatorLayout contentFrame = (CoordinatorLayout) findViewById(R.id.contentFrame);
39
-        assert contentFrame != null;
40
-        contentFrame.addView(mScreenStack);
41
-        mScreenStack.push(screen);
42
-
43
-        // Setup Toolbar after it's measured since icon height is dependent on Toolbar height
44
-        contentFrame.post(new Runnable() {
45
-            @Override
46
-            public void run() {
47
-                setupToolbar(screen);
48
-            }
49
-        });
50
-    }
51
-
52
-    protected void setupToolbar(Screen screen) {
53
-        StyleHelper.updateStyles(mToolbar, screen);
54
-    }
55
-
56
-    @Override
57
-    public void push(Screen screen) {
58
-        super.push(screen);
59
-        mScreenStack.push(screen);
60
-        StyleHelper.updateStyles(mToolbar, screen);
61
-    }
62
-
63
-    @Override
64
-    public Screen pop(String navigatorId) {
65
-        super.pop(navigatorId);
66
-        Screen popped = mScreenStack.pop();
67
-        StyleHelper.updateStyles(mToolbar, getCurrentScreen());
68
-        return popped;
69
-    }
70
-
71
-    @Override
72
-    public Screen popToRoot(String navigatorId) {
73
-        super.popToRoot(navigatorId);
74
-        Screen screen = mScreenStack.popToRoot();
75
-        StyleHelper.updateStyles(mToolbar, getCurrentScreen());
76
-        return screen;
77
-    }
78
-
79
-    @Override
80
-    public Screen resetTo(Screen screen) {
81
-        super.resetTo(screen);
82
-        Screen popped = mScreenStack.resetTo(screen);
83
-        StyleHelper.updateStyles(mToolbar, screen);
84
-        return popped;
85
-    }
86
-
87
-    @Override
88
-    public String getCurrentNavigatorId() {
89
-        return mNavigatorId;
90
-    }
91
-
92
-    @Override
93
-    public Screen getCurrentScreen() {
94
-        Screen currentScreen = super.getCurrentScreen();
95
-        if (currentScreen != null) {
96
-            return currentScreen;
97
-        }
98
-
99
-        return mScreenStack.peek();
100
-    }
101
-
102
-    @Override
103
-    public int getScreenStackSize() {
104
-        return mScreenStack.getStackSize();
105
-    }
106
-
107
-    @Override
108
-    protected void removeAllReactViews() {
109
-        mScreenStack.removeAllReactViews();
110
-    }
111
-
112
-    @Override
113
-    public void showFAB(ReadableMap params) {
114
-//        FloatingActionButton fab = new FloatingActionButton(this);
115
-//        fab.setImageDrawable(IconUtils.getIcon(this, params.getString("icon")));
116
-//        fab.setBackgroundColor(Color.parseColor(params.getString("backgroundColor")));
117
-//        fab.setImageResource(R.drawable.notification_background);
118
-//        CoordinatorLayout content = (CoordinatorLayout) findViewById(R.id.contentFrame);
119
-//        CoordinatorLayout.LayoutParams layoutParams = new CoordinatorLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
120
-//        layoutParams.gravity = Gravity.BOTTOM | Gravity.END;
121
-//        int m = (int) ImageUtils.convertDpToPixel(16, this);
122
-//        layoutParams.setMargins(m, m, m, m);
123
-//        content.addView(fab, layoutParams);
124
-    }
125
-}

+ 0
- 105
android/app/src/main/java/com/reactnativenavigation/activities/TabActivity.java View File

@@ -1,105 +0,0 @@
1
-package com.reactnativenavigation.activities;
2
-
3
-import android.support.v4.view.ViewPager;
4
-import android.view.Menu;
5
-import android.view.ViewGroup;
6
-
7
-import com.reactnativenavigation.R;
8
-import com.reactnativenavigation.adapters.ViewPagerAdapter;
9
-import com.reactnativenavigation.core.RctManager;
10
-import com.reactnativenavigation.core.objects.Screen;
11
-import com.reactnativenavigation.utils.StyleHelper;
12
-import com.reactnativenavigation.views.RnnTabLayout;
13
-import com.reactnativenavigation.views.RnnToolBar;
14
-
15
-import java.util.ArrayList;
16
-
17
-/**
18
- * This class is currently not supported and will be removed in future release.
19
- * Created by guyc on 02/04/16.
20
- */
21
-@Deprecated
22
-public class TabActivity extends BaseReactActivity {
23
-    public static final String EXTRA_SCREENS = "extraScreens";
24
-
25
-    private RnnTabLayout mTabLayout;
26
-    private ViewPager mViewPager;
27
-    private ViewPagerAdapter mAdapter;
28
-
29
-    @Override
30
-    protected void handleOnCreate() {
31
-        super.handleOnCreate();
32
-        mReactInstanceManager = RctManager.getInstance().getReactInstanceManager();
33
-
34
-        setContentView(R.layout.tab_activity);
35
-        mToolbar = (RnnToolBar) findViewById(R.id.toolbar);
36
-        mTabLayout = (RnnTabLayout) findViewById(R.id.tabLayout);
37
-        mViewPager = (ViewPager) findViewById(R.id.viewPager);
38
-
39
-        ArrayList<Screen> screens = (ArrayList<Screen>) getIntent().getSerializableExtra(EXTRA_SCREENS);
40
-
41
-        setupToolbar(screens);
42
-        setupViewPager(screens);
43
-    }
44
-
45
-    private void setupToolbar(ArrayList<Screen> screens) {
46
-        Screen initialScreen = screens.get(0);
47
-        mToolbar.setScreens(screens);
48
-        mToolbar.update(initialScreen);
49
-        setNavigationStyle(initialScreen);
50
-    }
51
-
52
-    public void setNavigationStyle(Screen screen) {
53
-        mTabLayout.setStyle(screen);
54
-    }
55
-
56
-    private void setupViewPager(ArrayList<Screen> screens) {
57
-        mAdapter = new ViewPagerAdapter(this, mViewPager, mToolbar, screens);
58
-        mViewPager.setAdapter(mAdapter);
59
-        mTabLayout.setupWithViewPager(mViewPager);
60
-        mTabLayout.setOnTabSelectedListener(mAdapter);
61
-        mAdapter.notifyDataSetChanged();
62
-    }
63
-
64
-    @Override
65
-    public boolean onCreateOptionsMenu(Menu menu) {
66
-        boolean ret = super.onCreateOptionsMenu(menu);
67
-        mToolbar.handleOnCreateOptionsMenuAsync();
68
-        return ret;
69
-    }
70
-
71
-    @Override
72
-    public void removeAllReactViews() {
73
-
74
-    }
75
-
76
-    @Override
77
-    public void push(Screen screen) {
78
-        super.push(screen);
79
-        StyleHelper.updateStyles(mToolbar, screen);
80
-        mAdapter.push(screen);
81
-    }
82
-
83
-    @Override
84
-    public Screen pop(String navigatorId) {
85
-        super.pop(navigatorId);
86
-        Screen popped = mAdapter.pop(navigatorId);
87
-        setNavigationStyle(getCurrentScreen());
88
-        return popped;
89
-    }
90
-
91
-    @Override
92
-    public Screen getCurrentScreen() {
93
-        return mAdapter.peek(getCurrentNavigatorId());
94
-    }
95
-
96
-    @Override
97
-    protected String getCurrentNavigatorId() {
98
-        return mAdapter.getNavigatorId(mViewPager.getCurrentItem());
99
-    }
100
-
101
-    @Override
102
-    public int getScreenStackSize() {
103
-        return mAdapter.getStackSizeForNavigatorId(getCurrentNavigatorId());
104
-    }
105
-}

+ 0
- 154
android/app/src/main/java/com/reactnativenavigation/adapters/ViewPagerAdapter.java View File

@@ -1,154 +0,0 @@
1
-package com.reactnativenavigation.adapters;
2
-
3
-import android.support.design.widget.TabLayout;
4
-import android.support.v4.view.PagerAdapter;
5
-import android.support.v4.view.ViewPager;
6
-import android.view.View;
7
-import android.view.ViewGroup;
8
-
9
-import com.facebook.react.bridge.Arguments;
10
-import com.facebook.react.bridge.WritableMap;
11
-import com.reactnativenavigation.activities.BaseReactActivity;
12
-import com.reactnativenavigation.core.RctManager;
13
-import com.reactnativenavigation.core.objects.Screen;
14
-import com.reactnativenavigation.utils.StyleHelper;
15
-import com.reactnativenavigation.views.RnnToolBar;
16
-import com.reactnativenavigation.views.ScreenStack;
17
-
18
-import java.util.ArrayList;
19
-import java.util.HashMap;
20
-import java.util.Map;
21
-
22
-/**
23
- * Created by guyc on 02/04/16.
24
- */
25
-public class ViewPagerAdapter extends PagerAdapter implements TabLayout.OnTabSelectedListener, ViewPager.OnPageChangeListener {
26
-
27
-    private static final String EVENT_ON_TAB_SELECTED = "OnTabSelected";
28
-
29
-    private ViewPager mViewPager;
30
-    private RnnToolBar mToolbar;
31
-    private final ArrayList<ScreenStack> mScreenStacks;
32
-    private final ArrayList<String> mNavigatorIds;
33
-    private final Map<String, ScreenStack> mStackByNavigatorId;
34
-    private int mCurrentPage = 0;
35
-
36
-    public ViewPagerAdapter(BaseReactActivity context, ViewPager viewPager, RnnToolBar toolbar,
37
-                            ArrayList<Screen> screens) {
38
-        mViewPager = viewPager;
39
-        mToolbar = toolbar;
40
-        mScreenStacks = new ArrayList<>();
41
-        mNavigatorIds = new ArrayList<>();
42
-        mStackByNavigatorId = new HashMap<>();
43
-        for (Screen screen : screens) {
44
-            ScreenStack stack = new ScreenStack(context);
45
-            stack.push(screen);
46
-            mScreenStacks.add(stack);
47
-            mNavigatorIds.add(screen.navigatorId);
48
-            mStackByNavigatorId.put(screen.navigatorId, stack);
49
-        }
50
-    }
51
-
52
-    public void push(Screen screen) {
53
-        ScreenStack stack = mStackByNavigatorId.get(screen.navigatorId);
54
-        Screen prevScreen = mScreenStacks.get(mCurrentPage).peek();
55
-        mToolbar.setupToolbarButtonsAsync(prevScreen, screen);
56
-        stack.push(screen);
57
-    }
58
-
59
-    public Screen pop(String navigatorId) {
60
-        ScreenStack stack = mStackByNavigatorId.get(navigatorId);
61
-        Screen oldScreen =  stack != null ? stack.pop() : null;
62
-        Screen newScreen = stack.peek();
63
-        mToolbar.setupToolbarButtonsAsync(oldScreen, newScreen);
64
-        return oldScreen;
65
-    }
66
-
67
-    public Screen peek(String navigatorId) {
68
-        ScreenStack stack = mStackByNavigatorId.get(navigatorId);
69
-        return stack != null ? stack.peek() : null;
70
-    }
71
-
72
-    @Override
73
-    public Object instantiateItem(ViewGroup container, int position) {
74
-        ScreenStack view = mScreenStacks.get(position);
75
-        container.addView(view);
76
-        return view;
77
-    }
78
-
79
-    @Override
80
-    public void destroyItem(ViewGroup container, int position, Object view) {
81
-        container.removeView((View) view);
82
-    }
83
-
84
-    @Override
85
-    public int getCount() {
86
-        return mScreenStacks.size();
87
-    }
88
-
89
-    @Override
90
-    public boolean isViewFromObject(View view, Object object) {
91
-        return view == object;
92
-    }
93
-
94
-    @Override
95
-    public CharSequence getPageTitle(int position) {
96
-        return mScreenStacks.get(position).peek().label;
97
-    }
98
-
99
-    @Override
100
-    public void onTabSelected(TabLayout.Tab tab) {
101
-        // Set the viewPager's current item
102
-        int position = tab.getPosition();
103
-        mViewPager.setCurrentItem(position);
104
-
105
-        // Set screen buttons
106
-        Screen prevScreen = mScreenStacks.get(mCurrentPage).peek();
107
-        Screen newScreen = mScreenStacks.get(position).peek();
108
-        mToolbar.setupToolbarButtonsAsync(prevScreen, newScreen);
109
-
110
-        // Set title
111
-        mToolbar.setTitle(newScreen.title == null ? "" : newScreen.title);
112
-
113
-        // Set navigation color
114
-        StyleHelper.updateStyles(mToolbar, newScreen);
115
-
116
-        // Send tab selected event
117
-        WritableMap params = Arguments.createMap();
118
-        Screen screen = mScreenStacks.get(position).peek();
119
-        RctManager.getInstance().sendEvent(EVENT_ON_TAB_SELECTED, screen, params);
120
-    }
121
-
122
-    @Override
123
-    public void onTabUnselected(TabLayout.Tab tab) {
124
-
125
-    }
126
-
127
-    @Override
128
-    public void onTabReselected(TabLayout.Tab tab) {
129
-
130
-    }
131
-
132
-    public String getNavigatorId(int position) {
133
-        return mNavigatorIds.get(position);
134
-    }
135
-
136
-    public int getStackSizeForNavigatorId(String activeNavigatorID) {
137
-        return mStackByNavigatorId.get(activeNavigatorID).getStackSize();
138
-    }
139
-
140
-    @Override
141
-    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
142
-
143
-    }
144
-
145
-    @Override
146
-    public void onPageSelected(int position) {
147
-        mCurrentPage = position;
148
-    }
149
-
150
-    @Override
151
-    public void onPageScrollStateChanged(int state) {
152
-
153
-    }
154
-}

+ 120
- 0
android/app/src/main/java/com/reactnativenavigation/animation/VisibilityAnimator.java View File

@@ -0,0 +1,120 @@
1
+package com.reactnativenavigation.animation;
2
+
3
+import android.animation.Animator;
4
+import android.animation.AnimatorListenerAdapter;
5
+import android.animation.ObjectAnimator;
6
+import android.support.v4.view.animation.LinearOutSlowInInterpolator;
7
+import android.view.View;
8
+
9
+import com.reactnativenavigation.views.ScrollDirectionListener;
10
+
11
+public class VisibilityAnimator {
12
+
13
+    public enum HideDirection {
14
+        Up, Down
15
+    }
16
+
17
+    private enum VisibilityState {
18
+        Hidden, AnimateHide, Shown, AnimateShow
19
+    }
20
+
21
+    private static final int SHOW_END_VALUE = 0;
22
+    private static final int DURATION = 300;
23
+
24
+    private final View view;
25
+    private final HideDirection hideDirection;
26
+    private final int hiddenEndValue;
27
+    private VisibilityState visibilityState = VisibilityState.Shown;
28
+
29
+    public VisibilityAnimator(View view, HideDirection hideDirection, int height) {
30
+        this.view = view;
31
+        this.hideDirection = hideDirection;
32
+        this.hiddenEndValue = hideDirection == HideDirection.Up ? -height : height;
33
+    }
34
+
35
+    public void onScrollChanged(ScrollDirectionListener.Direction scrollDirection) {
36
+        if (hideDirection == HideDirection.Down) {
37
+            handleDownHidingViews(scrollDirection);
38
+        } else {
39
+            handleUpHidingViews(scrollDirection);
40
+        }
41
+    }
42
+
43
+    public void setVisible(boolean visible, boolean animate) {
44
+        if (visible && isHiding()) {
45
+            show(animate);
46
+        } else if (!visible && isShowing()) {
47
+            hide(animate);
48
+        }
49
+    }
50
+
51
+    private void handleUpHidingViews(ScrollDirectionListener.Direction scrollDirection) {
52
+        if (scrollUp(scrollDirection) && !isShowing()) {
53
+            show(true);
54
+        } else if (scrollDown(scrollDirection) && !isHiding()) {
55
+            hide(true);
56
+        }
57
+    }
58
+
59
+    private void handleDownHidingViews(ScrollDirectionListener.Direction scrollDirection) {
60
+        if (scrollDown(scrollDirection) && !isHiding()) {
61
+            hide(true);
62
+        } else if (scrollUp(scrollDirection) && !isShowing()) {
63
+            show(true);
64
+        }
65
+    }
66
+
67
+    private void show(boolean animate) {
68
+        if (animate) {
69
+            ObjectAnimator animator = createAnimator(true);
70
+            animator.start();
71
+        } else {
72
+            visibilityState = VisibilityState.Shown;
73
+            view.setVisibility(View.VISIBLE);
74
+        }
75
+    }
76
+
77
+    private void hide(boolean animate) {
78
+        if (animate) {
79
+            ObjectAnimator animator = createAnimator(false);
80
+            animator.start();
81
+        } else {
82
+            visibilityState = VisibilityState.Hidden;
83
+            view.setVisibility(View.GONE);
84
+        }
85
+    }
86
+
87
+    private boolean scrollUp(ScrollDirectionListener.Direction scrollDirection) {
88
+        return scrollDirection == ScrollDirectionListener.Direction.Up;
89
+    }
90
+
91
+    private boolean scrollDown(ScrollDirectionListener.Direction scrollDirection) {
92
+        return scrollDirection == ScrollDirectionListener.Direction.Down;
93
+    }
94
+
95
+    private boolean isShowing() {
96
+        return visibilityState == VisibilityState.Shown || visibilityState == VisibilityState.AnimateShow;
97
+    }
98
+
99
+    private boolean isHiding() {
100
+        return visibilityState == VisibilityState.Hidden || visibilityState == VisibilityState.AnimateHide;
101
+    }
102
+
103
+    private ObjectAnimator createAnimator(final boolean show) {
104
+        ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, show ? SHOW_END_VALUE : hiddenEndValue);
105
+        animator.setDuration(DURATION);
106
+        animator.setInterpolator(new LinearOutSlowInInterpolator());
107
+        animator.addListener(new AnimatorListenerAdapter() {
108
+            @Override
109
+            public void onAnimationStart(Animator animation) {
110
+                visibilityState = show ? VisibilityState.AnimateShow : VisibilityState.AnimateHide;
111
+            }
112
+
113
+            @Override
114
+            public void onAnimationEnd(Animator animation) {
115
+                visibilityState = show ? VisibilityState.Shown : VisibilityState.Hidden;
116
+            }
117
+        });
118
+        return animator;
119
+    }
120
+}

+ 68
- 0
android/app/src/main/java/com/reactnativenavigation/bridge/BundleConverter.java View File

@@ -0,0 +1,68 @@
1
+package com.reactnativenavigation.bridge;
2
+
3
+import android.os.Bundle;
4
+
5
+import com.facebook.react.bridge.ReadableArray;
6
+import com.facebook.react.bridge.ReadableMap;
7
+import com.facebook.react.bridge.ReadableMapKeySetIterator;
8
+
9
+public class BundleConverter {
10
+    public static Bundle toBundle(ReadableMap map) {
11
+        Bundle bundle = new Bundle();
12
+        ReadableMapKeySetIterator it = map.keySetIterator();
13
+        while (it.hasNextKey()) {
14
+            String key = it.nextKey();
15
+            switch (map.getType(key)) {
16
+                case Null:
17
+                    break;
18
+                case Boolean:
19
+                    bundle.putBoolean(key, map.getBoolean(key));
20
+                    break;
21
+                case Number:
22
+                    bundle.putDouble(key, map.getDouble(key));
23
+                    break;
24
+                case String:
25
+                    bundle.putString(key, map.getString(key));
26
+                    break;
27
+                case Map:
28
+                    bundle.putBundle(key, toBundle(map.getMap(key)));
29
+                    break;
30
+                case Array:
31
+                    bundle.putBundle(key, toBundle(map.getArray(key)));
32
+                    break;
33
+                default:
34
+                    break;
35
+            }
36
+        }
37
+        return bundle;
38
+    }
39
+
40
+    public static Bundle toBundle(ReadableArray array) {
41
+        Bundle bundle = new Bundle();
42
+        for (int i = 0; i < array.size(); i++) {
43
+            String key = String.valueOf(i);
44
+            switch (array.getType(i)) {
45
+                case Null:
46
+                    break;
47
+                case Boolean:
48
+                    bundle.putBoolean(key, array.getBoolean(i));
49
+                    break;
50
+                case Number:
51
+                    bundle.putDouble(key, array.getDouble(i));
52
+                    break;
53
+                case String:
54
+                    bundle.putString(key, array.getString(i));
55
+                    break;
56
+                case Map:
57
+                    bundle.putBundle(key, toBundle(array.getMap(i)));
58
+                    break;
59
+                case Array:
60
+                    bundle.putBundle(key, toBundle(array.getArray(i)));
61
+                    break;
62
+                default:
63
+                    break;
64
+            }
65
+        }
66
+        return bundle;
67
+    }
68
+}

+ 41
- 0
android/app/src/main/java/com/reactnativenavigation/bridge/NavigationReactEventEmitter.java View File

@@ -0,0 +1,41 @@
1
+package com.reactnativenavigation.bridge;
2
+
3
+import com.facebook.react.bridge.Arguments;
4
+import com.facebook.react.bridge.ReactContext;
5
+import com.facebook.react.bridge.WritableMap;
6
+import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
7
+
8
+public class NavigationReactEventEmitter {
9
+
10
+    private static final String KEY_EVENT_ID = "id";
11
+    private static final String KEY_EVENT_TYPE = "type";
12
+    private static final String KEY_NAVIGATOR_EVENT_ID = "navigatorEventID";
13
+    private static final String EVENT_TYPE = "NavBarButtonPress";
14
+    private RCTDeviceEventEmitter eventEmitter;
15
+
16
+    public NavigationReactEventEmitter(ReactContext reactContext) {
17
+        this.eventEmitter = reactContext.getJSModule(RCTDeviceEventEmitter.class);
18
+    }
19
+
20
+    public void sendNavigatorEvent(String eventId, String navigatorEventId) {
21
+        WritableMap data = Arguments.createMap();
22
+        data.putString(KEY_EVENT_TYPE, EVENT_TYPE);
23
+        data.putString(KEY_EVENT_ID, eventId);
24
+        data.putString(KEY_NAVIGATOR_EVENT_ID, navigatorEventId);
25
+        eventEmitter.emit(navigatorEventId, data);
26
+    }
27
+
28
+    public void sendNavigatorEvent(String eventId, String navigatorEventId, WritableMap data) {
29
+        data.putString(KEY_NAVIGATOR_EVENT_ID, navigatorEventId);
30
+        data.putString(KEY_EVENT_ID, eventId);
31
+        eventEmitter.emit(navigatorEventId, data);
32
+    }
33
+
34
+    public void sendEvent(String eventId, String data) {
35
+        eventEmitter.emit(eventId, data);
36
+    }
37
+
38
+    public void sendEvent(String eventId, WritableMap data) {
39
+        eventEmitter.emit(eventId, data);
40
+    }
41
+}

+ 144
- 0
android/app/src/main/java/com/reactnativenavigation/bridge/NavigationReactModule.java View File

@@ -0,0 +1,144 @@
1
+package com.reactnativenavigation.bridge;
2
+
3
+import com.facebook.react.bridge.ReactApplicationContext;
4
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
5
+import com.facebook.react.bridge.ReactMethod;
6
+import com.facebook.react.bridge.ReadableArray;
7
+import com.facebook.react.bridge.ReadableMap;
8
+import com.reactnativenavigation.controllers.NavigationCommandsHandler;
9
+import com.reactnativenavigation.params.TitleBarButtonParams;
10
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
11
+import com.reactnativenavigation.params.parsers.TitleBarButtonParamsParser;
12
+import com.reactnativenavigation.params.parsers.TitleBarLeftButtonParamsParser;
13
+
14
+import java.util.List;
15
+
16
+/**
17
+ * The basic abstract components we will expose:
18
+ * BottomTabs (app) - boolean
19
+ * TopBar (per screen)
20
+ * - TitleBar
21
+ * - - RightButtons
22
+ * - - LeftButton
23
+ * - TopTabs (segmented control / view pager tabs)
24
+ * DeviceStatusBar (app) (colors are per screen)
25
+ * AndroidNavigationBar (app) (colors are per screen)
26
+ * SideMenu (app) - boolean, (menu icon is screen-based)
27
+ */
28
+public class NavigationReactModule extends ReactContextBaseJavaModule {
29
+    public static final String NAME = "NavigationReactModule";
30
+
31
+    public NavigationReactModule(ReactApplicationContext reactContext) {
32
+        super(reactContext);
33
+    }
34
+
35
+    @Override
36
+    public String getName() {
37
+        return NAME;
38
+    }
39
+
40
+    @ReactMethod
41
+    public void startApp(final ReadableMap params) {
42
+        NavigationCommandsHandler.startApp(BundleConverter.toBundle(params));
43
+    }
44
+
45
+    @ReactMethod
46
+    public void setScreenTitleBarTitle(String screenInstanceId, String title) {
47
+        NavigationCommandsHandler.setScreenTitleBarTitle(screenInstanceId, title);
48
+    }
49
+
50
+    @ReactMethod
51
+    public void setScreenTitleBarButtons(String screenInstanceId, String navigatorEventId,
52
+                                         ReadableArray rightButtonsParams, ReadableMap leftButtonParams) {
53
+        if (rightButtonsParams != null) {
54
+            setScreenTitleBarRightButtons(screenInstanceId, navigatorEventId, rightButtonsParams);
55
+        }
56
+
57
+        if (leftButtonParams != null) {
58
+            setScreenTitleBarLeftButton(screenInstanceId, navigatorEventId, leftButtonParams);
59
+        }
60
+    }
61
+
62
+    private void setScreenTitleBarRightButtons(String screenInstanceId, String navigatorEventId, ReadableArray rightButtonsParams) {
63
+        List<TitleBarButtonParams> rightButtons = new TitleBarButtonParamsParser()
64
+                .parseButtons(BundleConverter.toBundle(rightButtonsParams));
65
+        NavigationCommandsHandler.setScreenTitleBarRightButtons(screenInstanceId, navigatorEventId, rightButtons);
66
+    }
67
+
68
+    private void setScreenTitleBarLeftButton(String screenInstanceId, String navigatorEventId, ReadableMap leftButtonParams) {
69
+        TitleBarLeftButtonParams leftButton = new TitleBarLeftButtonParamsParser()
70
+                .parseSingleButton(BundleConverter.toBundle(leftButtonParams));
71
+        NavigationCommandsHandler.setScreenTitleBarLeftButtons(screenInstanceId, navigatorEventId, leftButton);
72
+    }
73
+
74
+    @ReactMethod
75
+    public void setTabBadge(final ReadableMap params) {
76
+    }
77
+
78
+    @ReactMethod
79
+    public void selectBottomTab(final ReadableMap params) {
80
+    }
81
+
82
+    @ReactMethod
83
+    public void toggleSideMenuVisible(boolean animated) {
84
+        NavigationCommandsHandler.toggleSideMenuVisible(animated);
85
+    }
86
+
87
+    @ReactMethod
88
+    public void setSideMenuVisible(boolean animated, boolean visible) {
89
+        NavigationCommandsHandler.setSideMenuVisible(animated, visible);
90
+    }
91
+
92
+    @ReactMethod
93
+    public void toggleTopBarVisible(final ReadableMap params) {
94
+    }
95
+
96
+    @ReactMethod
97
+    public void setTopBarVisible(String screenInstanceId, boolean hidden, boolean animated) {
98
+        NavigationCommandsHandler.setTopBarVisible(screenInstanceId, hidden, animated);
99
+    }
100
+
101
+    @ReactMethod
102
+    public void toggleBottomTabsVisible(final ReadableMap params) {
103
+    }
104
+
105
+    @ReactMethod
106
+    public void setBottomTabsVisible(boolean hidden, boolean animated) {
107
+        NavigationCommandsHandler.setBottomTabsVisible(hidden, animated);
108
+    }
109
+
110
+    @ReactMethod
111
+    public void push(final ReadableMap params) {
112
+        NavigationCommandsHandler.push(BundleConverter.toBundle(params));
113
+    }
114
+
115
+    @ReactMethod
116
+    public void pop(final ReadableMap params) {
117
+        NavigationCommandsHandler.pop(BundleConverter.toBundle(params));
118
+    }
119
+
120
+    @ReactMethod
121
+    public void popToRoot(final ReadableMap params) {
122
+        NavigationCommandsHandler.popToRoot(BundleConverter.toBundle(params));
123
+    }
124
+
125
+    @ReactMethod
126
+    public void newStack(final ReadableMap params) {
127
+        NavigationCommandsHandler.newStack(BundleConverter.toBundle(params));
128
+    }
129
+
130
+    @ReactMethod
131
+    public void showModal(final ReadableMap params) {
132
+        NavigationCommandsHandler.showModal(BundleConverter.toBundle(params));
133
+    }
134
+
135
+    @ReactMethod
136
+    public void dismissAllModals() {
137
+        NavigationCommandsHandler.dismissAllModals();
138
+    }
139
+
140
+    @ReactMethod
141
+    public void dismissTopModal() {
142
+        NavigationCommandsHandler.dismissTopModal();
143
+    }
144
+}

android/app/src/main/java/com/reactnativenavigation/packages/RnnPackage.java → android/app/src/main/java/com/reactnativenavigation/bridge/NavigationReactPackage.java View File

@@ -1,22 +1,21 @@
1
-package com.reactnativenavigation.packages;
1
+package com.reactnativenavigation.bridge;
2 2
 
3 3
 import com.facebook.react.ReactPackage;
4 4
 import com.facebook.react.bridge.JavaScriptModule;
5 5
 import com.facebook.react.bridge.NativeModule;
6 6
 import com.facebook.react.bridge.ReactApplicationContext;
7 7
 import com.facebook.react.uimanager.ViewManager;
8
-import com.reactnativenavigation.modules.RctActivityModule;
9 8
 
10 9
 import java.util.Arrays;
11 10
 import java.util.Collections;
12 11
 import java.util.List;
13 12
 
14
-public class RnnPackage implements ReactPackage {
13
+public class NavigationReactPackage implements ReactPackage {
15 14
 
16 15
     @Override
17 16
     public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
18 17
         return Arrays.<NativeModule>asList(
19
-                new RctActivityModule(reactContext)
18
+                new NavigationReactModule(reactContext)
20 19
         );
21 20
     }
22 21
 

+ 115
- 0
android/app/src/main/java/com/reactnativenavigation/controllers/Modal.java View File

@@ -0,0 +1,115 @@
1
+package com.reactnativenavigation.controllers;
2
+
3
+import android.app.Dialog;
4
+import android.content.DialogInterface;
5
+import android.support.v7.app.AppCompatActivity;
6
+import android.view.Window;
7
+import android.view.WindowManager;
8
+
9
+import com.reactnativenavigation.R;
10
+import com.reactnativenavigation.layouts.Layout;
11
+import com.reactnativenavigation.layouts.ScreenStackContainer;
12
+import com.reactnativenavigation.layouts.SingleScreenLayout;
13
+import com.reactnativenavigation.params.ScreenParams;
14
+import com.reactnativenavigation.params.TitleBarButtonParams;
15
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
16
+
17
+import java.util.List;
18
+
19
+public class Modal extends Dialog implements DialogInterface.OnDismissListener, ScreenStackContainer {
20
+
21
+    private final AppCompatActivity activity;
22
+    private final OnModalDismissedListener onModalDismissedListener;
23
+    private final ScreenParams screenParams;
24
+    private Layout layout;
25
+
26
+    public void setTopBarVisible(String screenInstanceId, boolean hidden, boolean animated) {
27
+        layout.setTopBarVisible(screenInstanceId, hidden, animated);
28
+    }
29
+
30
+    public void setTitleBarTitle(String screenInstanceId, String title) {
31
+        layout.setTitleBarTitle(screenInstanceId, title);
32
+    }
33
+
34
+    public void setTitleBarRightButtons(String screenInstanceId, String navigatorEventId, List<TitleBarButtonParams> titleBarButtons) {
35
+        layout.setTitleBarRightButtons(screenInstanceId, navigatorEventId, titleBarButtons);
36
+    }
37
+
38
+    public void setTitleBarLeftButton(String screenInstanceId, String navigatorEventId, TitleBarLeftButtonParams titleBarLeftButton) {
39
+        layout.setTitleBarLeftButton(screenInstanceId, navigatorEventId, titleBarLeftButton);
40
+    }
41
+
42
+    @Override
43
+    public boolean onTitleBarBackButtonClick() {
44
+        if (!layout.onBackPressed()) {
45
+            onBackPressed();
46
+        }
47
+        return true;
48
+    }
49
+
50
+    public void onSideMenuButtonClick() {
51
+    }
52
+
53
+    public interface OnModalDismissedListener {
54
+        void onModalDismissed(Modal modal);
55
+    }
56
+
57
+    public Modal(AppCompatActivity activity, OnModalDismissedListener onModalDismissedListener, ScreenParams screenParams) {
58
+        super(activity, R.style.Modal);
59
+        this.activity = activity;
60
+        this.onModalDismissedListener = onModalDismissedListener;
61
+        this.screenParams = screenParams;
62
+        createContent();
63
+    }
64
+
65
+    public AppCompatActivity getActivity() {
66
+        return activity;
67
+    }
68
+
69
+    private void createContent() {
70
+        setCancelable(true);
71
+        setOnDismissListener(this);
72
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
73
+        layout = new SingleScreenLayout(getActivity(), screenParams, this);
74
+        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
75
+        setContentView(layout.asView());
76
+    }
77
+
78
+    @Override
79
+    public void push(ScreenParams params) {
80
+        layout.push(params);
81
+    }
82
+
83
+    @Override
84
+    public void pop(ScreenParams screenParams) {
85
+        layout.pop(screenParams);
86
+    }
87
+
88
+    @Override
89
+    public void popToRoot(ScreenParams params) {
90
+        layout.popToRoot(params);
91
+    }
92
+
93
+    @Override
94
+    public void newStack(ScreenParams params) {
95
+        layout.newStack(params);
96
+    }
97
+
98
+    @Override
99
+    public void destroy() {
100
+        layout.destroy();
101
+    }
102
+
103
+    @Override
104
+    public void onBackPressed() {
105
+        if (!layout.onBackPressed()) {
106
+            super.onBackPressed();
107
+        }
108
+    }
109
+
110
+    @Override
111
+    public void onDismiss(DialogInterface dialog) {
112
+        destroy();
113
+        onModalDismissedListener.onModalDismissed(this);
114
+    }
115
+}

+ 81
- 34
android/app/src/main/java/com/reactnativenavigation/controllers/ModalController.java View File

@@ -1,63 +1,110 @@
1 1
 package com.reactnativenavigation.controllers;
2 2
 
3
-import android.support.annotation.Nullable;
3
+import android.support.v7.app.AppCompatActivity;
4 4
 
5
-import com.reactnativenavigation.modal.RnnModal;
6
-import com.reactnativenavigation.utils.RefUtils;
5
+import com.reactnativenavigation.layouts.ScreenStackContainer;
6
+import com.reactnativenavigation.params.ScreenParams;
7
+import com.reactnativenavigation.params.TitleBarButtonParams;
8
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
7 9
 
8
-import java.lang.ref.WeakReference;
10
+import java.util.List;
9 11
 import java.util.Stack;
10 12
 
11
-/**
12
- * Created by guyc on 06/05/16.
13
- */
14
-public class ModalController {
15
-    private static ModalController sInstance;
13
+public class ModalController implements ScreenStackContainer, Modal.OnModalDismissedListener {
14
+    private final AppCompatActivity activity;
15
+    private Stack<Modal> stack = new Stack<>();
16 16
 
17
-    private final Stack<WeakReference<RnnModal>> mModals;
17
+    public ModalController(AppCompatActivity activity) {
18
+        this.activity = activity;
19
+    }
20
+
21
+    public void showModal(ScreenParams screenParams) {
22
+        Modal modal = new Modal(activity, this, screenParams);
23
+        modal.show();
24
+        stack.add(modal);
25
+    }
18 26
 
19
-    private ModalController() {
20
-        mModals = new Stack<>();
27
+    public void dismissTopModal() {
28
+        if (isShowing()) {
29
+            stack.pop().dismiss();
30
+        }
21 31
     }
22 32
 
23
-    public static synchronized ModalController getInstance() {
24
-        if (sInstance == null) {
25
-            sInstance = new ModalController();
33
+    public void dismissAllModals() {
34
+        while (isShowing()) {
35
+            dismissTopModal();
26 36
         }
37
+    }
27 38
 
28
-        return sInstance;
39
+    public boolean isShowing() {
40
+        return !stack.empty();
29 41
     }
30 42
 
31
-    public void add(RnnModal modal) {
32
-        mModals.add(new WeakReference<>(modal));
43
+    public void push(ScreenParams params) {
44
+        stack.peek().push(params);
33 45
     }
34 46
 
35
-    public boolean isModalDisplayed() {
36
-        return mModals.size() != 0;
47
+    @Override
48
+    public void pop(ScreenParams screenParams) {
49
+        stack.peek().pop(screenParams);
37 50
     }
38 51
 
39
-    @Nullable
40
-    public RnnModal get() {
41
-        return isModalDisplayed() ? RefUtils.get(mModals.peek()) : null;
52
+    @Override
53
+    public void popToRoot(ScreenParams params) {
54
+        stack.peek().popToRoot(params);
42 55
     }
43 56
 
44
-    public void remove() {
45
-        if (isModalDisplayed()) {
46
-            mModals.pop();
57
+    @Override
58
+    public void newStack(ScreenParams params) {
59
+        stack.peek().newStack(params);
60
+    }
61
+
62
+    @Override
63
+    public void destroy() {
64
+        for (Modal modal : stack) {
65
+            modal.destroy();
66
+            modal.dismiss();
47 67
         }
68
+        stack.clear();
48 69
     }
49 70
 
50
-    public void dismissAllModals() {
51
-        while (isModalDisplayed()) {
52
-            dismissModal();
71
+    @Override
72
+    public void onModalDismissed(Modal modal) {
73
+        stack.remove(modal);
74
+    }
75
+
76
+    public void setTopBarVisible(String screenInstanceId, boolean hidden, boolean animated) {
77
+        for (Modal modal : stack) {
78
+            modal.setTopBarVisible(screenInstanceId, hidden, animated);
53 79
         }
54 80
     }
55 81
 
56
-    public void dismissModal() {
57
-        WeakReference<RnnModal> ref = mModals.pop();
58
-        RnnModal modal = RefUtils.get(ref);
59
-        if (modal != null) {
60
-            modal.dismiss();
82
+    public void setTitleBarTitle(String screenInstanceId, String title) {
83
+        for (Modal modal : stack) {
84
+            modal.setTitleBarTitle(screenInstanceId, title);
85
+        }
86
+    }
87
+
88
+    public void setTitleBarRightButtons(String screenInstanceId, String navigatorEventId, List<TitleBarButtonParams> titleBarButtons) {
89
+        for (Modal modal : stack) {
90
+            modal.setTitleBarRightButtons(screenInstanceId, navigatorEventId, titleBarButtons);
61 91
         }
62 92
     }
93
+
94
+    public void setTitleBarLeftButton(String screenInstanceId, String navigatorEventId, TitleBarLeftButtonParams titleBarLeftButton) {
95
+        for (Modal modal : stack) {
96
+            modal.setTitleBarLeftButton(screenInstanceId, navigatorEventId, titleBarLeftButton);
97
+        }
98
+    }
99
+
100
+    @Override
101
+    public boolean onTitleBarBackButtonClick() {
102
+        // Do nothing and let the layout handle the back button click
103
+        return false;
104
+    }
105
+
106
+    @Override
107
+    public void onSideMenuButtonClick() {
108
+        // Do nothing and let the layout handle the click
109
+    }
63 110
 }

+ 215
- 0
android/app/src/main/java/com/reactnativenavigation/controllers/NavigationActivity.java View File

@@ -0,0 +1,215 @@
1
+package com.reactnativenavigation.controllers;
2
+
3
+import android.content.Intent;
4
+import android.os.Bundle;
5
+import android.support.v7.app.AppCompatActivity;
6
+import android.view.KeyEvent;
7
+
8
+import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
9
+import com.reactnativenavigation.NavigationApplication;
10
+import com.reactnativenavigation.layouts.BottomTabsLayout;
11
+import com.reactnativenavigation.layouts.Layout;
12
+import com.reactnativenavigation.layouts.LayoutFactory;
13
+import com.reactnativenavigation.params.ActivityParams;
14
+import com.reactnativenavigation.params.ScreenParams;
15
+import com.reactnativenavigation.params.TitleBarButtonParams;
16
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
17
+import com.reactnativenavigation.react.JsDevReloadHandler;
18
+import com.reactnativenavigation.react.ReactGateway;
19
+import com.reactnativenavigation.react.RedboxPermission;
20
+import com.reactnativenavigation.utils.IntentUtils;
21
+
22
+import java.util.List;
23
+
24
+public class NavigationActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler, ReactGateway.OnJsDevReloadListener {
25
+
26
+    /**
27
+     * Although we start multiple activities, we make sure to pass Intent.CLEAR_TASK | Intent.NEW_TASK
28
+     * So that we actually have only 1 instance of the activity running at one time.
29
+     * We hold the currentActivity (resume->pause) so we know when we need to destroy the javascript context
30
+     * (when currentActivity is null, ie pause and destroy was called without resume).
31
+     * This is somewhat weird, and in the future we better use a single activity with changing contentView similar to ReactNative impl.
32
+     * Along with that, we should handle commands from the bridge using onNewIntent
33
+     */
34
+    static NavigationActivity currentActivity;
35
+
36
+    private ActivityParams activityParams;
37
+    private ModalController modalController;
38
+    private Layout layout;
39
+    private boolean waitingForNewJsContext = false;
40
+
41
+    @Override
42
+    protected void onCreate(Bundle savedInstanceState) {
43
+        super.onCreate(savedInstanceState);
44
+
45
+        if (!NavigationApplication.instance.isReactContextInitialized()) {
46
+            waitingForNewJsContext = true;
47
+            finish();
48
+            startActivity(IntentUtils.getLauncherIntent());
49
+            return;
50
+        }
51
+
52
+        RedboxPermission.permissionToShowRedboxIfNeeded(this);
53
+
54
+        activityParams = NavigationCommandsHandler.parseActivityParams(getIntent());
55
+
56
+        createLayout();
57
+        createModalController();
58
+    }
59
+
60
+    private void createModalController() {
61
+        modalController = new ModalController(this);
62
+    }
63
+
64
+    private void createLayout() {
65
+        layout = LayoutFactory.create(this, activityParams);
66
+        setContentView(layout.asView());
67
+    }
68
+
69
+    @Override
70
+    protected void onResume() {
71
+        super.onResume();
72
+        if (isFinishing()) {
73
+            return;
74
+        }
75
+
76
+        currentActivity = this;
77
+        NavigationApplication.instance.getReactGateway().onResumeActivity(this, this, this);
78
+    }
79
+
80
+    @Override
81
+    protected void onPause() {
82
+        super.onPause();
83
+        currentActivity = null;
84
+        NavigationApplication.instance.getReactGateway().onPauseActivity();
85
+    }
86
+
87
+    @Override
88
+    protected void onDestroy() {
89
+        destroyLayouts();
90
+        destroyJsIfNeeded();
91
+        super.onDestroy();
92
+    }
93
+
94
+    private void destroyLayouts() {
95
+        if (modalController != null) {
96
+            modalController.destroy();
97
+        }
98
+        if (layout != null) {
99
+            layout.destroy();
100
+        }
101
+    }
102
+
103
+    private void destroyJsIfNeeded() {
104
+        if (!waitingForNewJsContext && (currentActivity == null || currentActivity.isFinishing())) {
105
+            NavigationApplication.instance.getReactGateway().onDestroyApp();
106
+        }
107
+    }
108
+
109
+    @Override
110
+    public void onJsDevReload() {
111
+        modalController.destroy();
112
+        layout.destroy();
113
+    }
114
+
115
+    @Override
116
+    public void invokeDefaultOnBackPressed() {
117
+        super.onBackPressed();
118
+    }
119
+
120
+    @Override
121
+    public void onBackPressed() {
122
+        if (!layout.onBackPressed()) {
123
+            NavigationApplication.instance.getReactGateway().onBackPressed();
124
+        }
125
+    }
126
+
127
+    @Override
128
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
129
+        NavigationApplication.instance.getReactGateway().onActivityResult(requestCode, resultCode, data);
130
+    }
131
+
132
+    @Override
133
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
134
+        return JsDevReloadHandler.onKeyUp(getCurrentFocus(), keyCode) || super.onKeyUp(keyCode, event);
135
+    }
136
+
137
+    void push(ScreenParams params) {
138
+        if (modalController.isShowing()) {
139
+            modalController.push(params);
140
+        } else {
141
+            layout.push(params);
142
+        }
143
+    }
144
+
145
+    void pop(ScreenParams params) {
146
+        if (modalController.isShowing()) {
147
+            modalController.pop(params);
148
+        } else {
149
+            layout.pop(params);
150
+        }
151
+    }
152
+
153
+    void popToRoot(ScreenParams params) {
154
+        if (modalController.isShowing()) {
155
+            modalController.popToRoot(params);
156
+        } else {
157
+            layout.popToRoot(params);
158
+        }
159
+    }
160
+
161
+    void newStack(ScreenParams params) {
162
+        if (modalController.isShowing()) {
163
+            modalController.newStack(params);
164
+        } else {
165
+            layout.newStack(params);
166
+        }
167
+    }
168
+
169
+    void showModal(ScreenParams screenParams) {
170
+        modalController.showModal(screenParams);
171
+    }
172
+
173
+    void dismissTopModal() {
174
+        modalController.dismissTopModal();
175
+    }
176
+
177
+    void dismissAllModals() {
178
+        modalController.dismissAllModals();
179
+    }
180
+
181
+    //TODO all these setters should be combined to something like setStyle
182
+    void setTopBarVisible(String screenInstanceId, boolean hidden, boolean animated) {
183
+        layout.setTopBarVisible(screenInstanceId, hidden, animated);
184
+        modalController.setTopBarVisible(screenInstanceId, hidden, animated);
185
+    }
186
+
187
+    void setBottomTabsVisible(boolean hidden, boolean animated) {
188
+        if (layout instanceof BottomTabsLayout) {
189
+            ((BottomTabsLayout) layout).setBottomTabsVisible(hidden, animated);
190
+        }
191
+    }
192
+
193
+    void setTitleBarTitle(String screenInstanceId, String title) {
194
+        layout.setTitleBarTitle(screenInstanceId, title);
195
+        modalController.setTitleBarTitle(screenInstanceId, title);
196
+    }
197
+
198
+    void setTitleBarButtons(String screenInstanceId, String navigatorEventId, List<TitleBarButtonParams> titleBarButtons) {
199
+        layout.setTitleBarRightButtons(screenInstanceId, navigatorEventId, titleBarButtons);
200
+        modalController.setTitleBarRightButtons(screenInstanceId, navigatorEventId, titleBarButtons);
201
+    }
202
+
203
+    void setTitleBarLeftButton(String screenInstanceId, String navigatorEventId, TitleBarLeftButtonParams titleBarLeftButton) {
204
+        layout.setTitleBarLeftButton(screenInstanceId, navigatorEventId, titleBarLeftButton);
205
+        modalController.setTitleBarLeftButton(screenInstanceId, navigatorEventId, titleBarLeftButton);
206
+    }
207
+
208
+    public void toggleSideMenuVisible(boolean animated) {
209
+        layout.toggleSideMenuVisible(animated);
210
+    }
211
+
212
+    public void setSideMenuVisible(boolean animated, boolean visible) {
213
+        layout.setSideMenuVisible(animated, visible);
214
+    }
215
+}

+ 241
- 0
android/app/src/main/java/com/reactnativenavigation/controllers/NavigationCommandsHandler.java View File

@@ -0,0 +1,241 @@
1
+package com.reactnativenavigation.controllers;
2
+
3
+import android.content.Intent;
4
+import android.os.Bundle;
5
+
6
+import com.reactnativenavigation.NavigationApplication;
7
+import com.reactnativenavigation.params.ActivityParams;
8
+import com.reactnativenavigation.params.ScreenParams;
9
+import com.reactnativenavigation.params.TitleBarButtonParams;
10
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
11
+import com.reactnativenavigation.params.parsers.ActivityParamsParser;
12
+import com.reactnativenavigation.params.parsers.ScreenParamsParser;
13
+
14
+import java.util.List;
15
+
16
+public class NavigationCommandsHandler {
17
+
18
+    private static final String ACTIVITY_PARAMS_BUNDLE = "ACTIVITY_PARAMS_BUNDLE";
19
+
20
+    static ActivityParams parseActivityParams(Intent intent) {
21
+        return ActivityParamsParser.parse(intent.getBundleExtra(NavigationCommandsHandler.ACTIVITY_PARAMS_BUNDLE));
22
+    }
23
+
24
+    /**
25
+     * start a new activity with CLEAR_TASK | NEW_TASK
26
+     *
27
+     * @param params ActivityParams as bundle
28
+     */
29
+    public static void startApp(Bundle params) {
30
+        Intent intent = new Intent(NavigationApplication.instance, NavigationActivity.class);
31
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
32
+        intent.putExtra(ACTIVITY_PARAMS_BUNDLE, params);
33
+        NavigationApplication.instance.startActivity(intent);
34
+    }
35
+
36
+    public static void push(Bundle screenParams) {
37
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
38
+        if (currentActivity == null) {
39
+            return;
40
+        }
41
+
42
+        final ScreenParams params = ScreenParamsParser.parse(screenParams);
43
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
44
+            @Override
45
+            public void run() {
46
+                currentActivity.push(params);
47
+            }
48
+        });
49
+    }
50
+
51
+    public static void pop(Bundle screenParams) {
52
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
53
+        if (currentActivity == null) {
54
+            return;
55
+        }
56
+
57
+        final ScreenParams params = ScreenParamsParser.parse(screenParams);
58
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
59
+            @Override
60
+            public void run() {
61
+                currentActivity.pop(params);
62
+            }
63
+        });
64
+    }
65
+
66
+    public static void popToRoot(Bundle screenParams) {
67
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
68
+        if (currentActivity == null) {
69
+            return;
70
+        }
71
+
72
+        final ScreenParams params = ScreenParamsParser.parse(screenParams);
73
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
74
+            @Override
75
+            public void run() {
76
+                currentActivity.popToRoot(params);
77
+            }
78
+        });
79
+    }
80
+
81
+    public static void newStack(Bundle screenParams) {
82
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
83
+        if (currentActivity == null) {
84
+            return;
85
+        }
86
+
87
+        final ScreenParams params = ScreenParamsParser.parse(screenParams);
88
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
89
+            @Override
90
+            public void run() {
91
+                currentActivity.newStack(params);
92
+            }
93
+        });
94
+    }
95
+
96
+    public static void setTopBarVisible(final String screenInstanceID, final boolean hidden, final boolean animated) {
97
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
98
+        if (currentActivity == null) {
99
+            return;
100
+        }
101
+
102
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
103
+            @Override
104
+            public void run() {
105
+                currentActivity.setTopBarVisible(screenInstanceID, hidden, animated);
106
+            }
107
+        });
108
+    }
109
+
110
+    public static void setBottomTabsVisible(final boolean hidden, final boolean animated) {
111
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
112
+        if (currentActivity == null) {
113
+            return;
114
+        }
115
+
116
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
117
+            @Override
118
+            public void run() {
119
+                currentActivity.setBottomTabsVisible(hidden, animated);
120
+            }
121
+        });
122
+    }
123
+
124
+    public static void setScreenTitleBarTitle(final String screenInstanceId, final String title) {
125
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
126
+        if (currentActivity == null) {
127
+            return;
128
+        }
129
+
130
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
131
+            @Override
132
+            public void run() {
133
+                currentActivity.setTitleBarTitle(screenInstanceId, title);
134
+            }
135
+        });
136
+    }
137
+
138
+    public static void showModal(Bundle params) {
139
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
140
+        if (currentActivity == null) {
141
+            return;
142
+        }
143
+
144
+        final ScreenParams screenParams = ScreenParamsParser.parse(params);
145
+
146
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
147
+            @Override
148
+            public void run() {
149
+                currentActivity.showModal(screenParams);
150
+            }
151
+        });
152
+    }
153
+
154
+    public static void setScreenTitleBarRightButtons(final String screenInstanceId,
155
+                                                     final String navigatorEventId,
156
+                                                     final List<TitleBarButtonParams> titleBarButtons) {
157
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
158
+        if (currentActivity == null) {
159
+            return;
160
+        }
161
+
162
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
163
+            @Override
164
+            public void run() {
165
+                currentActivity.setTitleBarButtons(screenInstanceId, navigatorEventId, titleBarButtons);
166
+            }
167
+        });
168
+    }
169
+
170
+    public static void setScreenTitleBarLeftButtons(final String screenInstanceId,
171
+                                                     final String navigatorEventId,
172
+                                                     final TitleBarLeftButtonParams titleBarButtons) {
173
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
174
+        if (currentActivity == null) {
175
+            return;
176
+        }
177
+
178
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
179
+            @Override
180
+            public void run() {
181
+                currentActivity.setTitleBarLeftButton(screenInstanceId, navigatorEventId, titleBarButtons);
182
+            }
183
+        });
184
+    }
185
+
186
+    public static void dismissTopModal() {
187
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
188
+        if (currentActivity == null) {
189
+            return;
190
+        }
191
+
192
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
193
+            @Override
194
+            public void run() {
195
+                currentActivity.dismissTopModal();
196
+            }
197
+        });
198
+    }
199
+
200
+    public static void dismissAllModals() {
201
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
202
+        if (currentActivity == null) {
203
+            return;
204
+        }
205
+
206
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
207
+            @Override
208
+            public void run() {
209
+                currentActivity.dismissAllModals();
210
+            }
211
+        });
212
+    }
213
+
214
+    public static void toggleSideMenuVisible(final boolean animated) {
215
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
216
+        if (currentActivity == null) {
217
+            return;
218
+        }
219
+
220
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
221
+            @Override
222
+            public void run() {
223
+                currentActivity.toggleSideMenuVisible(animated);
224
+            }
225
+        });
226
+    }
227
+
228
+    public static void setSideMenuVisible(final boolean animated, final boolean visible) {
229
+        final NavigationActivity currentActivity = NavigationActivity.currentActivity;
230
+        if (currentActivity == null) {
231
+            return;
232
+        }
233
+
234
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
235
+            @Override
236
+            public void run() {
237
+                currentActivity.setSideMenuVisible(animated, visible);
238
+            }
239
+        });
240
+    }
241
+}

+ 57
- 0
android/app/src/main/java/com/reactnativenavigation/controllers/SplashActivity.java View File

@@ -0,0 +1,57 @@
1
+package com.reactnativenavigation.controllers;
2
+
3
+import android.graphics.Color;
4
+import android.os.Bundle;
5
+import android.support.annotation.LayoutRes;
6
+import android.support.annotation.Nullable;
7
+import android.support.v7.app.AppCompatActivity;
8
+import android.view.View;
9
+
10
+import com.reactnativenavigation.NavigationApplication;
11
+
12
+public abstract class SplashActivity extends AppCompatActivity {
13
+
14
+    @Override
15
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
16
+        super.onCreate(savedInstanceState);
17
+        setSplashLayout();
18
+
19
+        if (NavigationApplication.instance.isReactContextInitialized()) {
20
+            finish();
21
+        } else {
22
+            NavigationApplication.instance.startReactContext();
23
+        }
24
+    }
25
+
26
+    @Override
27
+    protected void onPause() {
28
+        super.onPause();
29
+        finish();
30
+    }
31
+
32
+    private void setSplashLayout() {
33
+        final int splashLayout = getSplashLayout();
34
+        if (splashLayout > 0) {
35
+            setContentView(splashLayout);
36
+        } else {
37
+            setContentView(createSplashLayout());
38
+        }
39
+    }
40
+
41
+    /**
42
+     * @return xml layout res id
43
+     */
44
+    @LayoutRes
45
+    public int getSplashLayout() {
46
+        return 0;
47
+    }
48
+
49
+    /**
50
+     * @return the layout you would like to show while react's js context loads
51
+     */
52
+    public View createSplashLayout() {
53
+        View view = new View(this);
54
+        view.setBackgroundColor(Color.WHITE);
55
+        return view;
56
+    }
57
+}

+ 0
- 204
android/app/src/main/java/com/reactnativenavigation/core/RctManager.java View File

@@ -1,204 +0,0 @@
1
-package com.reactnativenavigation.core;
2
-
3
-import android.app.Application;
4
-import android.util.Log;
5
-
6
-import com.facebook.react.LifecycleState;
7
-import com.facebook.react.ReactInstanceManager;
8
-import com.facebook.react.ReactPackage;
9
-import com.facebook.react.bridge.JavaJSExecutor;
10
-import com.facebook.react.bridge.ReactContext;
11
-import com.facebook.react.bridge.ReactContextBaseJavaModule;
12
-import com.facebook.react.bridge.WritableMap;
13
-import com.facebook.react.devsupport.DevSupportManager;
14
-import com.facebook.react.devsupport.ReactInstanceDevCommandsHandler;
15
-import com.reactnativenavigation.activities.BaseReactActivity;
16
-import com.reactnativenavigation.core.objects.Screen;
17
-import com.reactnativenavigation.utils.ContextProvider;
18
-import com.reactnativenavigation.utils.ReflectionUtils;
19
-
20
-import static com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
21
-
22
-/**
23
- * Created by guyc on 22/02/16.
24
- */
25
-public class RctManager {
26
-    private static final String TAG = "RctManager";
27
-    private static final String KEY_EVENT_ID = "id";
28
-    private static final String KEY_EVENT_TYPE = "type";
29
-    private static final String EVENT_TYPE = "NavBarButtonPress";
30
-    private static RctManager sInstance;
31
-
32
-    private ReactInstanceManager mReactManager;
33
-
34
-    private RctManager() {
35
-        // Singleton
36
-    }
37
-
38
-    public static synchronized RctManager getInstance() {
39
-        if (sInstance == null) {
40
-            sInstance = new RctManager();
41
-        }
42
-        return sInstance;
43
-    }
44
-
45
-    public ReactInstanceManager getReactInstanceManager() {
46
-        return mReactManager;
47
-    }
48
-
49
-    public boolean isInitialized() {
50
-        return mReactManager != null;
51
-    }
52
-
53
-    public void init(BaseReactActivity context) {
54
-        createReactInstanceManager(context);
55
-    }
56
-
57
-    /**
58
-     * Creates a React Instance Manager associated with this component name
59
-     */
60
-    public ReactInstanceManager createReactInstanceManager(BaseReactActivity reactActivity) {
61
-        ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
62
-                .setApplication((Application) reactActivity.getApplicationContext())
63
-                .setJSMainModuleName(reactActivity.getJSMainModuleName())
64
-                .setUseDeveloperSupport(reactActivity.getUseDeveloperSupport())
65
-                .setInitialLifecycleState(LifecycleState.BEFORE_RESUME);
66
-
67
-        for (ReactPackage reactPackage : reactActivity.getPackages()) {
68
-            builder.addPackage(reactPackage);
69
-        }
70
-
71
-        String jsBundleFile = reactActivity.getJSBundleFile();
72
-
73
-        if (jsBundleFile != null) {
74
-            builder.setJSBundleFile(jsBundleFile);
75
-        } else {
76
-            builder.setBundleAssetName(reactActivity.getBundleAssetName());
77
-        }
78
-
79
-        mReactManager = builder.build();
80
-        if (reactActivity.getUseDeveloperSupport()) {
81
-            setupDevSupportHandler(mReactManager);
82
-        }
83
-        return mReactManager;
84
-    }
85
-
86
-    /**
87
-     * Inject our CustomDevCommandsHandler to {@code reactInstanceManager} so we can detect when the bundle was
88
-     * parsed and is about to load.
89
-     * @param reactInstanceManager
90
-     */
91
-    private void setupDevSupportHandler(ReactInstanceManager reactInstanceManager) {
92
-        final ReactInstanceDevCommandsHandler devInterface = (ReactInstanceDevCommandsHandler)
93
-                ReflectionUtils.getDeclaredField(reactInstanceManager, "mDevInterface");
94
-        if (devInterface == null) {
95
-            Log.e(TAG, "Could not get field mDevInterface");
96
-            return;
97
-        }
98
-
99
-        // Create customDevCommandsHandler
100
-        CustomDevCommandsHandler customDevCommandsHandler = new CustomDevCommandsHandler(devInterface);
101
-        boolean success = ReflectionUtils.setField(reactInstanceManager, "mDevInterface", customDevCommandsHandler);
102
-        if (!success) {
103
-            Log.e(TAG, "Could not set field mDevInterface");
104
-            return;
105
-        }
106
-
107
-        // Set customDevCommandsHandler in devSupportManager. Fun =).
108
-        DevSupportManager devSupportManager = (DevSupportManager)
109
-                ReflectionUtils.getDeclaredField(reactInstanceManager, "mDevSupportManager");
110
-        if (devSupportManager == null) {
111
-            Log.e(TAG, "Could not get field mDevSupportManager");
112
-            return;
113
-        }
114
-
115
-        success = ReflectionUtils.setField(devSupportManager, "mReactInstanceCommandsHandler", customDevCommandsHandler);
116
-        if (!success) {
117
-            Log.e(TAG, "Could not set field mReactInstanceCommandsHandler");
118
-        }
119
-    }
120
-
121
-    public <T extends ReactContextBaseJavaModule> T getNativeModule(Class<T> nativeModuleClass) {
122
-        if (mReactManager == null || mReactManager.getCurrentReactContext() == null) {
123
-            return null;
124
-        }
125
-
126
-        return mReactManager.getCurrentReactContext().getNativeModule(nativeModuleClass);
127
-    }
128
-
129
-    /**
130
-     * Sends an event to JavaScript using <a href="https://facebook.github.io/react-native/docs/native-modules-android.html#sending-events-to-javascript">RCTDeviceEventEmitter</a>
131
-     * @param eventName Name of the event
132
-     * @param params Event params
133
-     * @param screen screen which should receive the event
134
-     */
135
-    public void sendEvent(String eventName, Screen screen, WritableMap params) {
136
-        RCTDeviceEventEmitter eventEmitter = getEventEmitter();
137
-        if (eventEmitter == null) {
138
-            return;
139
-        }
140
-
141
-        params.putString(KEY_EVENT_TYPE, EVENT_TYPE);
142
-        params.putString(KEY_EVENT_ID, eventName);
143
-        params.putString(Screen.KEY_NAVIGATOR_EVENT_ID, screen.navigatorEventId);
144
-        eventEmitter.emit(screen.navigatorEventId, params);
145
-    }
146
-
147
-    private RCTDeviceEventEmitter getEventEmitter() {
148
-        if (mReactManager == null) {
149
-            return null;
150
-        }
151
-
152
-        ReactContext currentReactContext = mReactManager.getCurrentReactContext();
153
-        if (currentReactContext == null) {
154
-            return null;
155
-        }
156
-
157
-        return currentReactContext.getJSModule(RCTDeviceEventEmitter.class);
158
-    }
159
-
160
-    public void onDestroy() {
161
-        mReactManager = null;
162
-        sInstance = null;
163
-    }
164
-
165
-    /**
166
-     * Proxy for {@link ReactInstanceDevCommandsHandler} used by {@link DevSupportManager} for requesting React
167
-     * instance recreation. Used to notify {@link BaseReactActivity} that the bundle has been reloaded.
168
-     */
169
-    private static class CustomDevCommandsHandler implements ReactInstanceDevCommandsHandler {
170
-        private ReactInstanceDevCommandsHandler mCommandsHandler;
171
-
172
-        public CustomDevCommandsHandler(ReactInstanceDevCommandsHandler commandsHandler) {
173
-            mCommandsHandler = commandsHandler;
174
-        }
175
-
176
-        @Override
177
-        public void onReloadWithJSDebugger(JavaJSExecutor.Factory proxyExecutorFactory) {
178
-            onJSBundleReloaded();
179
-            mCommandsHandler.onReloadWithJSDebugger(proxyExecutorFactory);
180
-        }
181
-
182
-        @Override
183
-        public void onJSBundleLoadedFromServer() {
184
-            onJSBundleReloaded();
185
-            mCommandsHandler.onJSBundleLoadedFromServer();
186
-        }
187
-
188
-        /**
189
-         * Detach previously added ReactRootViews before handling bundle.
190
-         */
191
-        private void onJSBundleReloaded() {
192
-            BaseReactActivity context = ContextProvider.getActivityContext();
193
-            if (context != null) {
194
-                context.onJSBundleReloaded();
195
-            }
196
-        }
197
-
198
-        @Override
199
-        public void toggleElementInspector() {
200
-            mCommandsHandler.toggleElementInspector();
201
-        }
202
-    }
203
-}
204
-

+ 0
- 80
android/app/src/main/java/com/reactnativenavigation/core/objects/Button.java View File

@@ -1,80 +0,0 @@
1
-package com.reactnativenavigation.core.objects;
2
-
3
-import android.content.Context;
4
-import android.graphics.drawable.Drawable;
5
-import android.view.MenuItem;
6
-
7
-import com.facebook.react.bridge.ReadableMap;
8
-import com.reactnativenavigation.utils.IconUtils;
9
-
10
-import java.io.Serializable;
11
-import java.util.HashMap;
12
-import java.util.Map;
13
-import java.util.concurrent.atomic.AtomicInteger;
14
-
15
-/**
16
- * Created by guyc on 08/04/16.
17
- */
18
-public class Button extends JsonObject implements Serializable {
19
-    private static final long serialVersionUID = -570145217281069067L;
20
-
21
-    private static final String KEY_ID = "id";
22
-    private static final String KEY_TITLE = "title";
23
-    private static final String KEY_ICON = "icon";
24
-    private static final String KEY_DISABLED = "disabled";
25
-    private static final String KEY_SHOW_AS_ACTION = "showAsAction";
26
-
27
-    public String id;
28
-    public String title;
29
-    private String mIconSource;
30
-    public boolean disabled;
31
-    public String showAsAction;
32
-
33
-    private static final AtomicInteger sAtomicIdGenerator = new AtomicInteger();
34
-    private static final Map<String, Integer> sStringToNumericId = new HashMap<>();
35
-
36
-    public Button(ReadableMap button) {
37
-        id = getString(button, KEY_ID);
38
-        title = getString(button, KEY_TITLE, "");
39
-        mIconSource = getString(button, KEY_ICON);
40
-        disabled = getBoolean(button, KEY_DISABLED);
41
-        showAsAction = getString(button, KEY_SHOW_AS_ACTION, "");
42
-    }
43
-
44
-    public boolean hasIcon() {
45
-        return mIconSource != null;
46
-    }
47
-
48
-    /**
49
-     * @param dimensions The requested icon dimensions
50
-     */
51
-    public Drawable getIcon(Context ctx, int dimensions) {
52
-       return IconUtils.getIcon(ctx, mIconSource, dimensions);
53
-    }
54
-
55
-    public int getItemId() {
56
-        if (sStringToNumericId.containsKey(id)) {
57
-            return sStringToNumericId.get(id);
58
-        }
59
-
60
-        int itemId = sAtomicIdGenerator.addAndGet(1);
61
-        sStringToNumericId.put(id, itemId);
62
-        return itemId;
63
-    }
64
-
65
-    /**
66
-     * Each button has a string id, defined in JS, which is used to identify the button when
67
-     * handling events.
68
-     * @param item Toolbar button
69
-     * @return Returns the event id associated with the given menu item
70
-     */
71
-    public static String getButtonEventId(MenuItem item) {
72
-        for (Map.Entry<String, Integer> entry : sStringToNumericId.entrySet()) {
73
-            if (entry.getValue() == item.getItemId()) {
74
-                return entry.getKey();
75
-            }
76
-        }
77
-
78
-        return null;
79
-    }
80
-}

+ 0
- 23
android/app/src/main/java/com/reactnativenavigation/core/objects/Drawer.java View File

@@ -1,23 +0,0 @@
1
-package com.reactnativenavigation.core.objects;
2
-
3
-import com.facebook.react.bridge.ReadableMap;
4
-
5
-import java.io.Serializable;
6
-
7
-public class Drawer extends JsonObject implements Serializable {
8
-    private static final long serialVersionUID = 982836768712398756L;
9
-
10
-    private static final String KEY_LEFT = "left";
11
-    private static final String KEY_RIGHT = "right";
12
-    private static final String KEY_DISABLE_OPEN_GESTURE = "disableOpenGesture";
13
-
14
-    public final Screen left;
15
-    public final Screen right;
16
-    public final boolean disableOpenGesture;
17
-
18
-    public Drawer(ReadableMap params) {
19
-        left = params.hasKey(KEY_LEFT) ? new Screen(params.getMap(KEY_LEFT)) : null;
20
-        right = params.hasKey(KEY_RIGHT) ? new Screen(params.getMap(KEY_RIGHT)) : null;
21
-        disableOpenGesture = getBoolean(params, KEY_DISABLE_OPEN_GESTURE);
22
-    }
23
-}

+ 0
- 35
android/app/src/main/java/com/reactnativenavigation/core/objects/JsonObject.java View File

@@ -1,35 +0,0 @@
1
-package com.reactnativenavigation.core.objects;
2
-
3
-import android.graphics.Color;
4
-
5
-import com.facebook.react.bridge.ReadableMap;
6
-
7
-/**
8
- * Created by guyc on 08/04/16.
9
- */
10
-public class JsonObject {
11
-
12
-    protected String getString(ReadableMap map, String key) {
13
-        return getString(map, key, null);
14
-    }
15
-
16
-    protected String getString(ReadableMap map, String key, String defaultValue) {
17
-        return map.hasKey(key) ? map.getString(key) : defaultValue;
18
-    }
19
-
20
-    protected int getInt(ReadableMap map, String key) {
21
-        return map.hasKey(key) ? map.getInt(key) : -1;
22
-    }
23
-
24
-    protected ReadableMap getMap(ReadableMap map, String key) {
25
-        return map.hasKey(key) ? map.getMap(key) : null;
26
-    }
27
-
28
-    protected Integer getColor(ReadableMap map, String key) {
29
-        return map.hasKey(key) ? Color.parseColor(map.getString(key)) : null;
30
-    }
31
-    
32
-    protected Boolean getBoolean(ReadableMap map, String key) {
33
-        return map.hasKey(key) && map.getBoolean(key);
34
-    }
35
-}

+ 0
- 144
android/app/src/main/java/com/reactnativenavigation/core/objects/Screen.java View File

@@ -1,144 +0,0 @@
1
-package com.reactnativenavigation.core.objects;
2
-
3
-import android.content.Context;
4
-import android.graphics.drawable.Drawable;
5
-import android.support.annotation.ColorInt;
6
-import android.support.annotation.NonNull;
7
-import android.support.annotation.Nullable;
8
-import android.util.Log;
9
-
10
-import com.facebook.react.bridge.ReadableArray;
11
-import com.facebook.react.bridge.ReadableMap;
12
-import com.facebook.react.bridge.ReadableNativeMap;
13
-import com.reactnativenavigation.utils.IconUtils;
14
-
15
-import java.io.Serializable;
16
-import java.util.ArrayList;
17
-import java.util.Collections;
18
-import java.util.HashMap;
19
-import java.util.List;
20
-
21
-/**
22
- * Created by guyc on 02/04/16.
23
- */
24
-public class Screen extends JsonObject implements Serializable {
25
-    private static final long serialVersionUID = -1033475305421107791L;
26
-
27
-    private static final String KEY_TITLE = "title";
28
-    private static final String KEY_SCREEN = "screen";
29
-    private static final String KEY_LABEL = "label";
30
-    public static final String KEY_SCREEN_INSTANCE_ID = "screenInstanceID";
31
-    public static final String KEY_NAVIGATOR_ID = "navigatorID";
32
-    public static final String KEY_NAVIGATOR_EVENT_ID = "navigatorEventID";
33
-    private static final String KEY_ICON = "icon";
34
-    private static final String KEY_NAVIGATOR_BUTTONS = "navigatorButtons";
35
-    private static final String KEY_RIGHT_BUTTONS = "rightButtons";
36
-    private static final String KEY_TOOL_BAR_STYLE = "navigatorStyle";
37
-    private static final String KEY_STATUS_BAR_COLOR = "statusBarColor";
38
-    private static final String KEY_TOOL_BAR_COLOR = "navBarBackgroundColor";
39
-    private static final String KEY_TOOL_BAR_HIDDEN = "navBarHidden";
40
-    private static final String KEY_NAVIGATION_BAR_COLOR = "navigationBarColor";
41
-    private static final String KEY_NAV_BAR_BUTTON_COLOR = "navBarButtonColor";
42
-    private static final String KEY_NAV_BAR_TEXT_COLOR = "navBarTextColor";
43
-    private static final String KEY_BACK_BUTTON_HIDDEN = "backButtonHidden";
44
-    private static final String KEY_TAB_NORMAL_TEXT_COLOR = "tabNormalTextColor";
45
-    private static final String KEY_TAB_SELECTED_TEXT_COLOR = "tabSelectedTextColor";
46
-    private static final String KEY_TAB_INDICATOR_COLOR = "tabIndicatorColor";
47
-    private static final String KEY_BOTTOM_TABS_HIDDEN = "tabBarHidden";
48
-    private static final String KEY_BOTTOM_TABS_HIDDEN_ON_SCROLL = "bottomTabsHiddenOnScroll";
49
-    private static final String KEY_PROPS = "passProps";
50
-
51
-    public String title;
52
-    public final String label;
53
-    public final String screenId;
54
-    public final String screenInstanceId;
55
-    public final String navigatorId;
56
-    public final String navigatorEventId;
57
-    public final String icon;
58
-    public ArrayList<Button> buttons;
59
-    public final boolean backButtonHidden;
60
-    public boolean bottomTabsHiddenOnScroll;
61
-    public HashMap<String, Object> passedProps = new HashMap<>();
62
-
63
-    // Navigation styling
64
-    @Nullable @ColorInt public Integer toolBarColor;
65
-    @Nullable public Boolean toolBarHidden;
66
-    @Nullable @ColorInt public Integer statusBarColor;
67
-    @Nullable @ColorInt public Integer navigationBarColor;
68
-    @Nullable @ColorInt public Integer navBarButtonColor;
69
-    @Nullable @ColorInt public Integer navBarTextColor;
70
-    @Nullable @ColorInt public Integer tabNormalTextColor;
71
-    @Nullable @ColorInt public Integer tabSelectedTextColor;
72
-    @Nullable @ColorInt public Integer tabIndicatorColor;
73
-    public Boolean bottomTabsHidden;
74
-
75
-    @NonNull
76
-    public List<Button> getButtons() {
77
-        return buttons == null ? Collections.<Button>emptyList() : buttons;
78
-    }
79
-
80
-    public Screen(ReadableMap screen) {
81
-        title = getString(screen, KEY_TITLE);
82
-        label = getString(screen, KEY_LABEL);
83
-        screenId = getString(screen, KEY_SCREEN);
84
-        screenInstanceId = getString(screen, KEY_SCREEN_INSTANCE_ID);
85
-        navigatorId = getString(screen, KEY_NAVIGATOR_ID);
86
-        navigatorEventId = getString(screen, KEY_NAVIGATOR_EVENT_ID);
87
-        icon = getString(screen, KEY_ICON);
88
-        if(screen.hasKey(KEY_PROPS)) {
89
-            passedProps = ((ReadableNativeMap) screen.getMap(KEY_PROPS)).toHashMap();
90
-        }
91
-        buttons = getButtons(screen);
92
-        backButtonHidden = getBoolean(screen, KEY_BACK_BUTTON_HIDDEN);
93
-        setToolbarStyle(screen);
94
-    }
95
-
96
-    public void setTitle(ReadableMap params) {
97
-        this.title = getString(params, KEY_TITLE);
98
-    }
99
-
100
-    public void setButtons(ReadableMap params) {
101
-        this.buttons = getButtons(params);
102
-    }
103
-
104
-    private ArrayList<Button> getButtons(ReadableMap screen) {
105
-        ArrayList<Button> ret = new ArrayList<>();
106
-        if (hasButtons(screen)) {
107
-            ReadableArray rightButtons = getRightButtons(screen);
108
-            for (int i = 0; i < rightButtons.size(); i++) {
109
-                ret.add(new Button(rightButtons.getMap(i)));
110
-            }
111
-        }
112
-        return ret;
113
-    }
114
-
115
-    private boolean hasButtons(ReadableMap screen) {
116
-        return screen.hasKey(KEY_RIGHT_BUTTONS) || screen.hasKey(KEY_NAVIGATOR_BUTTONS);
117
-    }
118
-
119
-    private ReadableArray getRightButtons(ReadableMap screen) {
120
-        return screen.hasKey(KEY_RIGHT_BUTTONS) ? screen.getArray(KEY_RIGHT_BUTTONS) :
121
-                screen.getMap(KEY_NAVIGATOR_BUTTONS).getArray(KEY_RIGHT_BUTTONS);
122
-    }
123
-
124
-    public Drawable getIcon(Context ctx) {
125
-        return IconUtils.getIcon(ctx, icon);
126
-    }
127
-
128
-    public void setToolbarStyle(ReadableMap screen) {
129
-        ReadableMap style = getMap(screen, KEY_TOOL_BAR_STYLE);
130
-        if (style != null) {
131
-            toolBarColor = getColor(style, KEY_TOOL_BAR_COLOR);
132
-            toolBarHidden = getBoolean(style, KEY_TOOL_BAR_HIDDEN);
133
-            statusBarColor = getColor(style, KEY_STATUS_BAR_COLOR);
134
-            navigationBarColor = getColor(style, KEY_NAVIGATION_BAR_COLOR);
135
-            navBarButtonColor = getColor(style, KEY_NAV_BAR_BUTTON_COLOR);
136
-            navBarTextColor = getColor(style, KEY_NAV_BAR_TEXT_COLOR);
137
-            tabNormalTextColor = getColor(style, KEY_TAB_NORMAL_TEXT_COLOR);
138
-            tabSelectedTextColor = getColor(style, KEY_TAB_SELECTED_TEXT_COLOR);
139
-            tabIndicatorColor = getColor(style, KEY_TAB_INDICATOR_COLOR);
140
-            bottomTabsHidden = getBoolean(style, KEY_BOTTOM_TABS_HIDDEN);
141
-            bottomTabsHiddenOnScroll = getBoolean(style, KEY_BOTTOM_TABS_HIDDEN_ON_SCROLL);
142
-        }
143
-    }
144
-}

+ 285
- 0
android/app/src/main/java/com/reactnativenavigation/layouts/BottomTabsLayout.java View File

@@ -0,0 +1,285 @@
1
+package com.reactnativenavigation.layouts;
2
+
3
+import android.support.annotation.NonNull;
4
+import android.support.annotation.Nullable;
5
+import android.support.v7.app.AppCompatActivity;
6
+import android.view.View;
7
+import android.widget.RelativeLayout;
8
+
9
+import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
10
+import com.reactnativenavigation.params.ActivityParams;
11
+import com.reactnativenavigation.params.ScreenParams;
12
+import com.reactnativenavigation.params.SideMenuParams;
13
+import com.reactnativenavigation.params.TitleBarButtonParams;
14
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
15
+import com.reactnativenavigation.screens.ScreenStack;
16
+import com.reactnativenavigation.views.BottomTabs;
17
+import com.reactnativenavigation.views.SideMenu;
18
+
19
+import java.util.List;
20
+
21
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
22
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
23
+
24
+public class BottomTabsLayout extends RelativeLayout implements Layout, AHBottomNavigation.OnTabSelectedListener {
25
+
26
+    private final AppCompatActivity activity;
27
+    private ActivityParams params;
28
+    private BottomTabs bottomTabs;
29
+    private ScreenStack[] screenStacks;
30
+    private final SideMenuParams sideMenuParams;
31
+    private @Nullable SideMenu sideMenu;
32
+    private int currentStackIndex = 0;
33
+
34
+    public BottomTabsLayout(AppCompatActivity activity, ActivityParams params) {
35
+        super(activity);
36
+        this.activity = activity;
37
+        this.params = params;
38
+        this.sideMenuParams = params.sideMenuParams;
39
+        screenStacks = new ScreenStack[params.tabParams.size()];
40
+        createLayout();
41
+    }
42
+
43
+    private void createLayout() {
44
+        createSideMenu();
45
+        createBottomTabs();
46
+        addBottomTabs();
47
+        addScreenStacks();
48
+        showInitialScreenStack();
49
+    }
50
+
51
+    private void createSideMenu() {
52
+        if (sideMenuParams == null) {
53
+            return;
54
+        }
55
+        sideMenu = new SideMenu(getContext(), sideMenuParams);
56
+        RelativeLayout.LayoutParams lp = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
57
+        addView(sideMenu, lp);
58
+    }
59
+
60
+    private void addScreenStacks() {
61
+        for (int i = 0; i < screenStacks.length; i++) {
62
+            createAndAddScreens(i);
63
+        }
64
+    }
65
+
66
+    private void createAndAddScreens(int position) {
67
+        ScreenParams screenParams = params.tabParams.get(position);
68
+        ScreenStack newStack = new ScreenStack(activity, getScreenStackParent(), screenParams.getNavigatorId(), this);
69
+        newStack.pushInitialScreen(screenParams, createScreenLayoutParams(screenParams));
70
+        screenStacks[position] = newStack;
71
+    }
72
+
73
+    private RelativeLayout getScreenStackParent() {
74
+        return sideMenu == null ? this : sideMenu.getContentContainer();
75
+    }
76
+
77
+    @NonNull
78
+    private LayoutParams createScreenLayoutParams(ScreenParams params) {
79
+        LayoutParams lp = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
80
+        if (params.styleParams.drawScreenAboveBottomTabs) {
81
+            lp.addRule(RelativeLayout.ABOVE, bottomTabs.getId());
82
+        }
83
+        return lp;
84
+    }
85
+
86
+    private void createBottomTabs() {
87
+        bottomTabs = new BottomTabs(getContext());
88
+        bottomTabs.addTabs(params.tabParams, this);
89
+    }
90
+
91
+    private void addBottomTabs() {
92
+        LayoutParams lp = new LayoutParams(MATCH_PARENT, WRAP_CONTENT);
93
+        lp.addRule(ALIGN_PARENT_BOTTOM);
94
+        getScreenStackParent().addView(bottomTabs, lp);
95
+    }
96
+
97
+    private void showInitialScreenStack() {
98
+        showStackAndUpdateStyle(screenStacks[0]);
99
+    }
100
+
101
+    @Override
102
+    public View asView() {
103
+        return this;
104
+    }
105
+
106
+    @Override
107
+    public boolean onBackPressed() {
108
+        if (getCurrentScreenStack().handleBackPressInJs()) {
109
+            return true;
110
+        }
111
+
112
+        if (getCurrentScreenStack().canPop()) {
113
+            getCurrentScreenStack().pop(true);
114
+            setBottomTabsStyleFromCurrentScreen();
115
+            return true;
116
+        } else {
117
+            return false;
118
+        }
119
+    }
120
+
121
+    @Override
122
+    public void setTopBarVisible(String screenInstanceId, boolean hidden, boolean animated) {
123
+        for (int i = 0; i < bottomTabs.getItemsCount(); i++) {
124
+            screenStacks[i].setScreenTopBarVisible(screenInstanceId, hidden, animated);
125
+        }
126
+    }
127
+
128
+    public void setBottomTabsVisible(boolean hidden, boolean animated) {
129
+        bottomTabs.setVisibility(hidden, animated);
130
+    }
131
+
132
+    @Override
133
+    public void setTitleBarTitle(String screenInstanceId, String title) {
134
+        for (int i = 0; i < bottomTabs.getItemsCount(); i++) {
135
+            screenStacks[i].setScreenTitleBarTitle(screenInstanceId, title);
136
+        }
137
+    }
138
+
139
+    @Override
140
+    public void setTitleBarRightButtons(String screenInstanceId, String navigatorEventId, List<TitleBarButtonParams> titleBarButtons) {
141
+        for (int i = 0; i < bottomTabs.getItemsCount(); i++) {
142
+            screenStacks[i].setScreenTitleBarRightButtons(screenInstanceId, navigatorEventId, titleBarButtons);
143
+        }
144
+    }
145
+
146
+    @Override
147
+    public void setTitleBarLeftButton(String screenInstanceId, String navigatorEventId, TitleBarLeftButtonParams titleBarLeftButtonParams) {
148
+        for (int i = 0; i < bottomTabs.getItemsCount(); i++) {
149
+            screenStacks[i].setScreenTitleBarLeftButton(screenInstanceId, navigatorEventId, titleBarLeftButtonParams);
150
+        }
151
+    }
152
+
153
+    @Override
154
+    public void toggleSideMenuVisible(boolean animated) {
155
+        if (sideMenu != null) {
156
+            sideMenu.toggleVisible(animated);
157
+        }
158
+    }
159
+
160
+    @Override
161
+    public void setSideMenuVisible(boolean animated, boolean visible) {
162
+        if (sideMenu != null) {
163
+            sideMenu.setVisible(visible, animated);
164
+        }
165
+    }
166
+
167
+    @Override
168
+    public void push(ScreenParams screenParams) {
169
+        ScreenStack screenStack = getScreenStack(screenParams.getNavigatorId());
170
+        if (screenStack == null) {
171
+            return;
172
+        }
173
+
174
+        if (isCurrentStack(screenStack)) {
175
+            screenStack.push(screenParams, createScreenLayoutParams(screenParams));
176
+            bottomTabs.setStyleFromScreen(screenParams.styleParams);
177
+        } else {
178
+            screenStack.push(screenParams, createScreenLayoutParams(screenParams));
179
+        }
180
+    }
181
+
182
+    @Override
183
+    public void pop(ScreenParams screenParams) {
184
+        getCurrentScreenStack().pop(screenParams.animateScreenTransitions, new ScreenStack.OnScreenPop() {
185
+            @Override
186
+            public void onScreenPopAnimationEnd() {
187
+                setBottomTabsStyleFromCurrentScreen();
188
+            }
189
+        });
190
+    }
191
+
192
+    @Override
193
+    public void popToRoot(ScreenParams params) {
194
+        getCurrentScreenStack().popToRoot(params.animateScreenTransitions);
195
+        setBottomTabsStyleFromCurrentScreen();
196
+    }
197
+
198
+    @Override
199
+    public void newStack(ScreenParams params) {
200
+        ScreenStack currentScreenStack = getCurrentScreenStack();
201
+        currentScreenStack.destroy();
202
+        removeView(currentScreenStack.peek());
203
+
204
+        ScreenStack newStack = new ScreenStack(activity, getScreenStackParent(), params.getNavigatorId(), this);
205
+        LayoutParams lp = createScreenLayoutParams(params);
206
+        newStack.pushInitialScreen(params, lp);
207
+        screenStacks[currentStackIndex] = newStack;
208
+
209
+        bottomTabs.setStyleFromScreen(params.styleParams);
210
+    }
211
+
212
+    @Override
213
+    public void destroy() {
214
+        for (ScreenStack screenStack : screenStacks) {
215
+            screenStack.destroy();
216
+        }
217
+
218
+        if (sideMenu != null) {
219
+            sideMenu.destroy();
220
+        }
221
+    }
222
+
223
+    @Override
224
+    public boolean onTabSelected(int position, boolean wasSelected) {
225
+        hideCurrentStack();
226
+
227
+        ScreenStack newStack = screenStacks[position];
228
+        showStackAndUpdateStyle(newStack);
229
+        currentStackIndex = position;
230
+
231
+        return true;
232
+    }
233
+
234
+    private void showStackAndUpdateStyle(ScreenStack newStack) {
235
+        newStack.show();
236
+        bottomTabs.setStyleFromScreen(newStack.getCurrentScreenStyleParams());
237
+    }
238
+
239
+    private void hideCurrentStack() {
240
+        ScreenStack currentScreenStack = getCurrentScreenStack();
241
+        currentScreenStack.hide();
242
+    }
243
+
244
+    private ScreenStack getCurrentScreenStack() {
245
+        return screenStacks[currentStackIndex];
246
+    }
247
+
248
+    private @Nullable ScreenStack getScreenStack(String navigatorId) {
249
+        for (ScreenStack screenStack : screenStacks) {
250
+            if (screenStack.getNavigatorId().equals(navigatorId)) {
251
+                return screenStack;
252
+            }
253
+        }
254
+        return null;
255
+    }
256
+
257
+    private boolean isCurrentStack(ScreenStack screenStack) {
258
+        return getCurrentScreenStack() == screenStack;
259
+    }
260
+
261
+    private void setBottomTabsStyleFromCurrentScreen() {
262
+        bottomTabs.setStyleFromScreen(getCurrentScreenStack().getCurrentScreenStyleParams());
263
+    }
264
+
265
+    @Override
266
+    public boolean onTitleBarBackButtonClick() {
267
+        if (getCurrentScreenStack().canPop()) {
268
+            getCurrentScreenStack().pop(true, new ScreenStack.OnScreenPop() {
269
+                @Override
270
+                public void onScreenPopAnimationEnd() {
271
+                    setBottomTabsStyleFromCurrentScreen();
272
+                }
273
+            });
274
+            return true;
275
+        }
276
+        return false;
277
+    }
278
+
279
+    @Override
280
+    public void onSideMenuButtonClick() {
281
+        if (sideMenu != null) {
282
+            sideMenu.openDrawer();
283
+        }
284
+    }
285
+}

+ 26
- 0
android/app/src/main/java/com/reactnativenavigation/layouts/Layout.java View File

@@ -0,0 +1,26 @@
1
+package com.reactnativenavigation.layouts;
2
+
3
+import android.view.View;
4
+
5
+import com.reactnativenavigation.params.TitleBarButtonParams;
6
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
7
+
8
+import java.util.List;
9
+
10
+public interface Layout extends ScreenStackContainer {
11
+    View asView();
12
+
13
+    boolean onBackPressed();
14
+
15
+    void setTopBarVisible(String screenInstanceId, boolean hidden, boolean animated);
16
+
17
+    void setTitleBarTitle(String screenInstanceId, String title);
18
+
19
+    void setTitleBarRightButtons(String screenInstanceId, String navigatorEventId, List<TitleBarButtonParams> titleBarButtons);
20
+
21
+    void setTitleBarLeftButton(String screenInstanceId, String navigatorEventId, TitleBarLeftButtonParams titleBarLeftButtonParams);
22
+
23
+    void toggleSideMenuVisible(boolean animated);
24
+
25
+    void setSideMenuVisible(boolean animated, boolean visible);
26
+}

+ 36
- 0
android/app/src/main/java/com/reactnativenavigation/layouts/LayoutFactory.java View File

@@ -0,0 +1,36 @@
1
+package com.reactnativenavigation.layouts;
2
+
3
+import android.support.v7.app.AppCompatActivity;
4
+import android.util.Log;
5
+
6
+import com.reactnativenavigation.params.ActivityParams;
7
+
8
+public class LayoutFactory {
9
+    public static Layout create(AppCompatActivity activity, ActivityParams params) {
10
+        switch (params.type) {
11
+            case TabBased:
12
+                return createBottomTabsScreenLayout(activity, params);
13
+            case SingleScreen:
14
+            default:
15
+                return createSingleScreenLayout(activity, params);
16
+        }
17
+    }
18
+
19
+    private static Layout createSingleScreenLayout(AppCompatActivity activity, ActivityParams params) {
20
+        return new SingleScreenLayout(activity, params.sideMenuParams, params.screenParams);
21
+    }
22
+
23
+    private static Layout createBottomTabsScreenLayout(AppCompatActivity activity, ActivityParams params) {
24
+        if (params.tabParams.size() > 5) {
25
+            removeAllButTheFirst5Tabs(params);
26
+        }
27
+        return new BottomTabsLayout(activity, params);
28
+    }
29
+
30
+    private static void removeAllButTheFirst5Tabs(ActivityParams params) {
31
+        Log.e("Navigation", "LayoutFactory:createBottomTabsScreenLayout() does not support more than 5 tabs, currently");
32
+        while (params.tabParams.size() > 5) {
33
+            params.tabParams.remove(params.tabParams.size() - 1);
34
+        }
35
+    }
36
+}

+ 16
- 0
android/app/src/main/java/com/reactnativenavigation/layouts/ScreenStackContainer.java View File

@@ -0,0 +1,16 @@
1
+package com.reactnativenavigation.layouts;
2
+
3
+import com.reactnativenavigation.params.ScreenParams;
4
+import com.reactnativenavigation.views.LeftButtonOnClickListener;
5
+
6
+public interface ScreenStackContainer extends LeftButtonOnClickListener {
7
+    void push(ScreenParams screenParams);
8
+
9
+    void pop(ScreenParams screenParams);
10
+
11
+    void popToRoot(ScreenParams params);
12
+
13
+    void newStack(ScreenParams params);
14
+
15
+    void destroy();
16
+}

+ 168
- 0
android/app/src/main/java/com/reactnativenavigation/layouts/SingleScreenLayout.java View File

@@ -0,0 +1,168 @@
1
+package com.reactnativenavigation.layouts;
2
+
3
+import android.support.annotation.Nullable;
4
+import android.support.v7.app.AppCompatActivity;
5
+import android.view.View;
6
+import android.widget.RelativeLayout;
7
+
8
+import com.reactnativenavigation.params.ScreenParams;
9
+import com.reactnativenavigation.params.SideMenuParams;
10
+import com.reactnativenavigation.params.TitleBarButtonParams;
11
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
12
+import com.reactnativenavigation.screens.ScreenStack;
13
+import com.reactnativenavigation.views.LeftButtonOnClickListener;
14
+import com.reactnativenavigation.views.SideMenu;
15
+
16
+import java.util.List;
17
+
18
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
19
+
20
+public class SingleScreenLayout extends RelativeLayout implements Layout {
21
+
22
+    private final AppCompatActivity activity;
23
+    private final ScreenParams screenParams;
24
+    private final SideMenuParams sideMenuParams;
25
+    private ScreenStack stack;
26
+    private LeftButtonOnClickListener leftButtonOnClickListener;
27
+    private @Nullable SideMenu sideMenu;
28
+
29
+    public SingleScreenLayout(AppCompatActivity activity, ScreenParams screenParams,
30
+                              LeftButtonOnClickListener leftButtonOnClickListener) {
31
+        this(activity, null, screenParams);
32
+        this.leftButtonOnClickListener = leftButtonOnClickListener;
33
+    }
34
+
35
+    public SingleScreenLayout(AppCompatActivity activity, @Nullable SideMenuParams sideMenuParams, ScreenParams screenParams) {
36
+        super(activity);
37
+        this.activity = activity;
38
+        this.screenParams = screenParams;
39
+        this.sideMenuParams = sideMenuParams;
40
+        createLayout();
41
+    }
42
+
43
+    private void createLayout() {
44
+        if (sideMenuParams == null) {
45
+            createStack(this);
46
+        } else {
47
+            sideMenu = createSideMenu();
48
+            createStack(sideMenu.getContentContainer());
49
+        }
50
+    }
51
+
52
+    private SideMenu createSideMenu() {
53
+        SideMenu sideMenu = new SideMenu(getContext(), sideMenuParams);
54
+        RelativeLayout.LayoutParams lp = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
55
+        addView(sideMenu, lp);
56
+        return sideMenu;
57
+    }
58
+
59
+    private void createStack(RelativeLayout parent) {
60
+        if (stack != null) {
61
+            stack.destroy();
62
+        }
63
+        stack = new ScreenStack(activity, parent, screenParams.getNavigatorId(), this);
64
+        LayoutParams lp = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
65
+        stack.pushInitialScreen(screenParams, lp);
66
+        stack.show();
67
+    }
68
+
69
+    @Override
70
+    public boolean onBackPressed() {
71
+        if (stack.handleBackPressInJs()) {
72
+            return true;
73
+        }
74
+
75
+        if (stack.canPop()) {
76
+            stack.pop(true);
77
+            return true;
78
+        } else {
79
+            return false;
80
+        }
81
+    }
82
+
83
+    @Override
84
+    public void destroy() {
85
+        stack.destroy();
86
+        if (sideMenu != null) {
87
+            sideMenu.destroy();
88
+        }
89
+    }
90
+
91
+    @Override
92
+    public void push(ScreenParams params) {
93
+        LayoutParams lp = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
94
+        stack.push(params, lp);
95
+    }
96
+
97
+    @Override
98
+    public void pop(ScreenParams params) {
99
+        stack.pop(params.animateScreenTransitions);
100
+    }
101
+
102
+    @Override
103
+    public void popToRoot(ScreenParams params) {
104
+        stack.popToRoot(params.animateScreenTransitions);
105
+    }
106
+
107
+    @Override
108
+    public void newStack(ScreenParams params) {
109
+        RelativeLayout parent = sideMenu == null ? this : sideMenu.getContentContainer();
110
+        createStack(parent);
111
+    }
112
+
113
+    @Override
114
+    public void setTopBarVisible(String screenInstanceID, boolean visible, boolean animate) {
115
+        stack.setScreenTopBarVisible(screenInstanceID, visible, animate);
116
+    }
117
+
118
+    @Override
119
+    public void setTitleBarTitle(String screenInstanceId, String title) {
120
+        stack.setScreenTitleBarTitle(screenInstanceId, title);
121
+    }
122
+
123
+    @Override
124
+    public View asView() {
125
+        return this;
126
+    }
127
+
128
+    @Override
129
+    public void setTitleBarRightButtons(String screenInstanceId, String navigatorEventId,
130
+                                        List<TitleBarButtonParams> titleBarRightButtons) {
131
+        stack.setScreenTitleBarRightButtons(screenInstanceId, navigatorEventId, titleBarRightButtons);
132
+    }
133
+
134
+    @Override
135
+    public void setTitleBarLeftButton(String screenInstanceId, String navigatorEventId, TitleBarLeftButtonParams titleBarLeftButtonParams) {
136
+        stack.setScreenTitleBarLeftButton(screenInstanceId, navigatorEventId, titleBarLeftButtonParams);
137
+    }
138
+
139
+    @Override
140
+    public void toggleSideMenuVisible(boolean animated) {
141
+        if (sideMenu != null) {
142
+            sideMenu.toggleVisible(animated);
143
+        }
144
+    }
145
+
146
+    @Override
147
+    public void setSideMenuVisible(boolean animated, boolean visible) {
148
+        if (sideMenu != null) {
149
+            sideMenu.setVisible(visible, animated);
150
+        }
151
+    }
152
+
153
+    @Override
154
+    public boolean onTitleBarBackButtonClick() {
155
+        if (leftButtonOnClickListener != null) {
156
+            return leftButtonOnClickListener.onTitleBarBackButtonClick();
157
+        }
158
+
159
+        return onBackPressed();
160
+    }
161
+
162
+    @Override
163
+    public void onSideMenuButtonClick() {
164
+        if (sideMenu != null) {
165
+            sideMenu.openDrawer();
166
+        }
167
+    }
168
+}

+ 0
- 109
android/app/src/main/java/com/reactnativenavigation/modal/RnnModal.java View File

@@ -1,109 +0,0 @@
1
-package com.reactnativenavigation.modal;
2
-
3
-import android.annotation.SuppressLint;
4
-import android.app.Dialog;
5
-import android.content.Context;
6
-import android.content.DialogInterface;
7
-import android.support.annotation.Nullable;
8
-import android.view.LayoutInflater;
9
-import android.view.View;
10
-import android.view.Window;
11
-import android.view.WindowManager;
12
-import android.view.animation.Animation;
13
-import android.view.animation.AnimationUtils;
14
-
15
-import com.reactnativenavigation.R;
16
-import com.reactnativenavigation.activities.BaseReactActivity;
17
-import com.reactnativenavigation.controllers.ModalController;
18
-import com.reactnativenavigation.core.objects.Screen;
19
-import com.reactnativenavigation.utils.ContextProvider;
20
-import com.reactnativenavigation.utils.SdkSupports;
21
-import com.reactnativenavigation.utils.StyleHelper;
22
-import com.reactnativenavigation.views.RctView;
23
-import com.reactnativenavigation.views.RnnToolBar;
24
-import com.reactnativenavigation.views.ScreenStack;
25
-
26
-/**
27
- * Created by guyc on 02/05/16.
28
- */
29
-public class RnnModal extends Dialog implements DialogInterface.OnDismissListener {
30
-
31
-    private ScreenStack mScreenStack;
32
-    private View mContentView;
33
-    private RnnToolBar mToolBar;
34
-
35
-    public RnnModal(BaseReactActivity context, Screen screen) {
36
-        super(context, R.style.Modal);
37
-        ModalController.getInstance().add(this);
38
-        init(context, screen);
39
-    }
40
-
41
-    @SuppressLint("InflateParams")
42
-    private void init(final Context context, Screen screen) {
43
-        requestWindowFeature(Window.FEATURE_NO_TITLE);
44
-        mContentView = LayoutInflater.from(context).inflate(R.layout.modal_layout, null, false);
45
-        mToolBar = (RnnToolBar) mContentView.findViewById(R.id.toolbar);
46
-        mScreenStack = (ScreenStack) mContentView.findViewById(R.id.screenStack);
47
-        setContentView(mContentView);
48
-        mToolBar.update(screen);
49
-        mScreenStack.push(screen, new RctView.OnDisplayedListener() {
50
-            @Override
51
-            public void onDisplayed() {
52
-                Animation animation = AnimationUtils.loadAnimation(context, R.anim.slide_up);
53
-                mContentView.setAnimation(animation);
54
-                mContentView.animate();
55
-            }
56
-        });
57
-
58
-        // Set navigation colors
59
-        if (SdkSupports.lollipop()) {
60
-            Window window = getWindow();
61
-            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
62
-            StyleHelper.setWindowStyle(window, context.getApplicationContext(), screen);
63
-        }
64
-        setOnDismissListener(this);
65
-    }
66
-
67
-    public void push(Screen screen) {
68
-        mScreenStack.push(screen);
69
-        mToolBar.update(screen);
70
-    }
71
-
72
-    public Screen pop() {
73
-        Screen popped = mScreenStack.pop();
74
-        if (mScreenStack.isEmpty()) {
75
-            dismiss();
76
-        }
77
-        Screen currentScreen = getCurrentScreen();
78
-        if (currentScreen != null) {
79
-            mToolBar.update(currentScreen);
80
-        }
81
-        return popped;
82
-    }
83
-
84
-    @Nullable
85
-    public Screen getCurrentScreen() {
86
-        return mScreenStack.isEmpty() ? null : mScreenStack.peek();
87
-    }
88
-
89
-    @Override
90
-    public void onBackPressed() {
91
-        if (mScreenStack.getStackSize() == 1) {
92
-            super.onBackPressed();
93
-        } else {
94
-            pop();
95
-        }
96
-    }
97
-
98
-    @Override
99
-    public void onDismiss(DialogInterface dialog) {
100
-        mScreenStack.removeAllReactViews();
101
-        ModalController.getInstance().remove();
102
-        // After modal is dismissed, update Toolbar with screen from parent activity or previously displayed modal
103
-        BaseReactActivity context = ContextProvider.getActivityContext();
104
-        if (context != null) {
105
-            Screen currentScreen = context.getCurrentScreen();
106
-            StyleHelper.updateStyles(mToolBar, currentScreen);
107
-        }
108
-    }
109
-}

+ 0
- 346
android/app/src/main/java/com/reactnativenavigation/modules/RctActivityModule.java View File

@@ -1,346 +0,0 @@
1
-package com.reactnativenavigation.modules;
2
-
3
-import android.app.Activity;
4
-import android.content.Intent;
5
-import android.os.Bundle;
6
-
7
-import com.facebook.react.bridge.ReactApplicationContext;
8
-import com.facebook.react.bridge.ReactContextBaseJavaModule;
9
-import com.facebook.react.bridge.ReactMethod;
10
-import com.facebook.react.bridge.ReadableArray;
11
-import com.facebook.react.bridge.ReadableMap;
12
-import com.facebook.react.bridge.ReadableNativeMap;
13
-import com.reactnativenavigation.activities.BaseReactActivity;
14
-import com.reactnativenavigation.activities.BottomTabActivity;
15
-import com.reactnativenavigation.activities.RootActivity;
16
-import com.reactnativenavigation.activities.SingleScreenActivity;
17
-import com.reactnativenavigation.controllers.ModalController;
18
-import com.reactnativenavigation.core.objects.Drawer;
19
-import com.reactnativenavigation.core.objects.Screen;
20
-import com.reactnativenavigation.modal.RnnModal;
21
-import com.reactnativenavigation.utils.BridgeUtils;
22
-import com.reactnativenavigation.utils.ContextProvider;
23
-
24
-import java.util.ArrayList;
25
-
26
-public class RctActivityModule extends ReactContextBaseJavaModule {
27
-    public static final String REACT_CLASS = "RctActivity";
28
-    private static final String KEY_NAVIGATOR_ID = "navigatorID";
29
-
30
-    public RctActivityModule(ReactApplicationContext reactContext) {
31
-        super(reactContext);
32
-    }
33
-
34
-    @Override
35
-    public String getName() {
36
-        return REACT_CLASS;
37
-    }
38
-
39
-    @ReactMethod
40
-    public void startTabBasedApp(ReadableArray screens, ReadableMap style, ReadableMap drawerParams) {
41
-        Activity context = ContextProvider.getActivityContext();
42
-        if (context != null && !context.isFinishing()) {
43
-            Intent intent = new Intent(context, BottomTabActivity.class);
44
-            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
45
-
46
-            Bundle extras = new Bundle();
47
-            extras.putSerializable(BottomTabActivity.EXTRA_SCREENS, createScreens(screens));
48
-            if (drawerParams != null) {
49
-                extras.putSerializable(BottomTabActivity.DRAWER_PARAMS, new Drawer(drawerParams));
50
-            }
51
-            if (style != null) {
52
-                BridgeUtils.addMapToBundle(((ReadableNativeMap) style).toHashMap(), extras);
53
-            }
54
-            intent.putExtras(extras);
55
-
56
-            context.startActivity(intent);
57
-            //TODO add abstract isRoot() instead of instanceof?
58
-            if (ContextProvider.getActivityContext() instanceof RootActivity) {
59
-                context.overridePendingTransition(0, 0);
60
-            }
61
-
62
-            // Dismiss modals associated with previous activity
63
-            ModalController.getInstance().dismissAllModals();
64
-        }
65
-    }
66
-
67
-    private ArrayList<Screen> createScreens(ReadableArray screens) {
68
-        ArrayList<Screen> ret = new ArrayList<>();
69
-        for (int i = 0; i < screens.size(); i++) {
70
-            ret.add(new Screen(screens.getMap(i)));
71
-        }
72
-        return ret;
73
-    }
74
-
75
-    @ReactMethod
76
-    public void startSingleScreenApp(ReadableMap screen, ReadableMap drawerParams) {
77
-        BaseReactActivity context = ContextProvider.getActivityContext();
78
-        if (context != null && !context.isFinishing()) {
79
-            Intent intent = new Intent(context, SingleScreenActivity.class);
80
-            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
81
-
82
-            Bundle extras = new Bundle();
83
-            extras.putSerializable(SingleScreenActivity.EXTRA_SCREEN, new Screen(screen));
84
-            if (drawerParams != null) {
85
-                extras.putSerializable(SingleScreenActivity.DRAWER_PARAMS, new Drawer(drawerParams));
86
-            }
87
-            intent.putExtras(extras);
88
-
89
-            context.startActivity(intent);
90
-            if (ContextProvider.getActivityContext() instanceof RootActivity) {
91
-                context.overridePendingTransition(0, 0);
92
-            }
93
-
94
-            // Dismiss modals associated with previous activity
95
-            ModalController.getInstance().dismissAllModals();
96
-        }
97
-    }
98
-
99
-    @ReactMethod
100
-    public void setNavigatorButtons(final ReadableMap buttons) {
101
-        final BaseReactActivity context = ContextProvider.getActivityContext();
102
-        if (context == null || context.isFinishing()) {
103
-            return;
104
-        }
105
-        context.runOnUiThread(new Runnable() {
106
-            @Override
107
-            public void run() {
108
-                context.setNavigationButtons(buttons);
109
-            }
110
-        });
111
-    }
112
-
113
-    @ReactMethod
114
-    public void setNavigatorTitle(final ReadableMap title) {
115
-        final BaseReactActivity context = ContextProvider.getActivityContext();
116
-        if (context == null || context.isFinishing()) {
117
-            return;
118
-        }
119
-        context.runOnUiThread(new Runnable() {
120
-            @Override
121
-            public void run() {
122
-                context.setNavigationTitle(title);
123
-            }
124
-        });
125
-    }
126
-
127
-    @ReactMethod
128
-    public void setTabBadge(final ReadableMap params) {
129
-        final BaseReactActivity context = ContextProvider.getActivityContext();
130
-        if (context == null || context.isFinishing()) {
131
-            return;
132
-        }
133
-        context.runOnUiThread(new Runnable() {
134
-            @Override
135
-            public void run() {
136
-                ((BottomTabActivity) context).setTabBadge(params);
137
-            }
138
-        });
139
-    }
140
-
141
-    @ReactMethod
142
-    public void switchToTab(final ReadableMap params) {
143
-        final BaseReactActivity context = ContextProvider.getActivityContext();
144
-        if (context == null || context.isFinishing()) {
145
-            return;
146
-        }
147
-        context.runOnUiThread(new Runnable() {
148
-            @Override
149
-            public void run() {
150
-                ((BottomTabActivity) context).switchToTab(params);
151
-            }
152
-        });
153
-    }
154
-
155
-    @ReactMethod
156
-    public void toggleDrawer(final ReadableMap params) {
157
-        final BaseReactActivity context = ContextProvider.getActivityContext();
158
-        if (context == null || context.isFinishing()) {
159
-            return;
160
-        }
161
-        context.runOnUiThread(new Runnable() {
162
-            @Override
163
-            public void run() {
164
-                context.toggleDrawer(params);
165
-            }
166
-        });
167
-    }
168
-
169
-    @ReactMethod
170
-    public void toggleNavigationBar(final ReadableMap params) {
171
-        final BaseReactActivity context = ContextProvider.getActivityContext();
172
-        if (context == null || context.isFinishing()) {
173
-            return;
174
-        }
175
-        context.runOnUiThread(new Runnable() {
176
-            @Override
177
-            public void run() {
178
-                context.toggleNavigationBar(params);
179
-            }
180
-        });
181
-    }
182
-
183
-    @ReactMethod
184
-    public void toggleNavigatorTabs(final ReadableMap params) {
185
-        final BaseReactActivity context = ContextProvider.getActivityContext();
186
-        if (context == null || context.isFinishing()) {
187
-            return;
188
-        }
189
-        context.runOnUiThread(new Runnable() {
190
-            @Override
191
-            public void run() {
192
-                ((BottomTabActivity) context).toggleTabs(params);
193
-            }
194
-        });
195
-    }
196
-
197
-    @ReactMethod
198
-    public void navigatorPush(final ReadableMap skreen) {
199
-        final Screen screen = new Screen(skreen);
200
-        final BaseReactActivity context = ContextProvider.getActivityContext();
201
-        if (context == null || context.isFinishing()) {
202
-            return;
203
-        }
204
-
205
-        // First, check if the screen should be pushed to a Modal
206
-        ModalController modalController = ModalController.getInstance();
207
-        if (modalController.isModalDisplayed()) {
208
-            final RnnModal modal = modalController.get();
209
-            if (modal != null) {
210
-                context.runOnUiThread(new Runnable() {
211
-                    @Override
212
-                    public void run() {
213
-                        modal.push(screen);
214
-                    }
215
-                });
216
-            }
217
-            return;
218
-        }
219
-
220
-        // No Modal is displayed, Push to activity
221
-        context.runOnUiThread(new Runnable() {
222
-            @Override
223
-            public void run() {
224
-                context.push(screen);
225
-            }
226
-        });
227
-    }
228
-
229
-    @ReactMethod
230
-    public void navigatorPop(final ReadableMap navigator) {
231
-        final String navigatorId = navigator.getString(KEY_NAVIGATOR_ID);
232
-        final BaseReactActivity context = ContextProvider.getActivityContext();
233
-        if (context == null || context.isFinishing()) {
234
-            return;
235
-        }
236
-
237
-        // First, check if the screen should be popped from a Modal
238
-        ModalController modalController = ModalController.getInstance();
239
-        if (modalController.isModalDisplayed()) {
240
-            final RnnModal modal = modalController.get();
241
-            if (modal != null) {
242
-                context.runOnUiThread(new Runnable() {
243
-                    @Override
244
-                    public void run() {
245
-                        modal.pop();
246
-                    }
247
-                });
248
-            }
249
-            return;
250
-        } else {
251
-            context.runOnUiThread(new Runnable() {
252
-                @Override
253
-                public void run() {
254
-                    context.pop(navigatorId);
255
-                }
256
-            });
257
-        }
258
-    }
259
-
260
-    @ReactMethod
261
-    public void navigatorPopToRoot(final ReadableMap params) {
262
-        final BaseReactActivity context = ContextProvider.getActivityContext();
263
-        if (context == null || context.isFinishing()) {
264
-            return;
265
-        }
266
-
267
-        final String navigatorID = params.getString(KEY_NAVIGATOR_ID);
268
-        context.runOnUiThread(new Runnable() {
269
-            @Override
270
-            public void run() {
271
-                context.popToRoot(navigatorID);
272
-            }
273
-        });
274
-    }
275
-
276
-    @ReactMethod
277
-    public void navigatorResetTo(final ReadableMap skreen) {
278
-        final BaseReactActivity context = ContextProvider.getActivityContext();
279
-        if (context == null || context.isFinishing()) {
280
-            return;
281
-        }
282
-
283
-        final Screen screen = new Screen(skreen);
284
-        context.runOnUiThread(new Runnable() {
285
-            @Override
286
-            public void run() {
287
-                context.resetTo(screen);
288
-            }
289
-        });
290
-    }
291
-
292
-    @ReactMethod
293
-    public void showModal(final ReadableMap screen) {
294
-        final BaseReactActivity context = ContextProvider.getActivityContext();
295
-        if (context != null && !context.isFinishing()) {
296
-            context.runOnUiThread(new Runnable() {
297
-                @Override
298
-                public void run() {
299
-                    new RnnModal(context, new Screen(screen)).show();
300
-                }
301
-            });
302
-        }
303
-    }
304
-
305
-    @ReactMethod
306
-    public void dismissAllModals(final ReadableMap params) {
307
-        final BaseReactActivity context = ContextProvider.getActivityContext();
308
-        if (context != null && !context.isFinishing()) {
309
-            context.runOnUiThread(new Runnable() {
310
-                @Override
311
-                public void run() {
312
-                    ModalController modalController = ModalController.getInstance();
313
-                    if (modalController.isModalDisplayed()) {
314
-                        modalController.dismissAllModals();
315
-                    }
316
-                }
317
-            });
318
-        }
319
-    }
320
-
321
-    /**
322
-     * Dismisses the top modal (the last modal pushed).
323
-     */
324
-    @ReactMethod
325
-    public void dismissModal() {
326
-        ModalController modalController = ModalController.getInstance();
327
-        if (modalController.isModalDisplayed()) {
328
-            modalController.dismissModal();
329
-        }
330
-    }
331
-
332
-    @ReactMethod
333
-    public void showFAB(final ReadableMap params) {
334
-        final BaseReactActivity context = ContextProvider.getActivityContext();
335
-        if (context == null || context.isFinishing()) {
336
-            return;
337
-        }
338
-        context.runOnUiThread(new Runnable() {
339
-            @Override
340
-            public void run() {
341
-                context.showFAB(params);
342
-            }
343
-        });
344
-    }
345
-
346
-}

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

@@ -0,0 +1,14 @@
1
+package com.reactnativenavigation.params;
2
+
3
+import java.util.List;
4
+
5
+public class ActivityParams {
6
+    public enum Type {
7
+        SingleScreen, TabBased
8
+    }
9
+
10
+    public Type type;
11
+    public ScreenParams screenParams;
12
+    public List<ScreenParams> tabParams;
13
+    public SideMenuParams sideMenuParams;
14
+}

+ 13
- 0
android/app/src/main/java/com/reactnativenavigation/params/AppStyle.java View File

@@ -0,0 +1,13 @@
1
+package com.reactnativenavigation.params;
2
+
3
+import android.os.Bundle;
4
+
5
+import com.reactnativenavigation.params.parsers.StyleParamsParser;
6
+
7
+public class AppStyle {
8
+    public static StyleParams appStyle;
9
+
10
+    public static void setAppStyle(Bundle params) {
11
+        appStyle = new StyleParamsParser(params.getBundle("appStyle")).parse();
12
+    }
13
+}

+ 27
- 0
android/app/src/main/java/com/reactnativenavigation/params/NavigationParams.java View File

@@ -0,0 +1,27 @@
1
+package com.reactnativenavigation.params;
2
+
3
+import android.os.Bundle;
4
+
5
+public class NavigationParams {
6
+    public static final String SCREEN_INSTANCE_ID = "screenInstanceID";
7
+    public static final String NAVIGATOR_ID = "navigatorID";
8
+    public static final String NAVIGATOR_EVENT_ID = "navigatorEventID";
9
+
10
+    public String screenInstanceId;
11
+    public String navigatorId;
12
+    public String navigatorEventId;
13
+
14
+    public NavigationParams(Bundle bundle) {
15
+        screenInstanceId = bundle.getString(SCREEN_INSTANCE_ID);
16
+        navigatorId = bundle.getString(NAVIGATOR_ID);
17
+        navigatorEventId = bundle.getString(NAVIGATOR_EVENT_ID);
18
+    }
19
+
20
+    public Bundle toBundle() {
21
+        Bundle b = new Bundle();
22
+        b.putString(SCREEN_INSTANCE_ID, screenInstanceId);
23
+        b.putString(NAVIGATOR_ID, navigatorId);
24
+        b.putString(NAVIGATOR_EVENT_ID, navigatorEventId);
25
+        return b;
26
+    }
27
+}

+ 44
- 0
android/app/src/main/java/com/reactnativenavigation/params/ScreenParams.java View File

@@ -0,0 +1,44 @@
1
+package com.reactnativenavigation.params;
2
+
3
+import android.graphics.drawable.Drawable;
4
+import android.os.Bundle;
5
+
6
+import java.util.List;
7
+
8
+public class ScreenParams {
9
+    public String screenId;
10
+    public List<TitleBarButtonParams> rightButtons;
11
+    public TitleBarLeftButtonParams leftButton;
12
+    public boolean overrideBackPressInJs;
13
+    public String title;
14
+    public StyleParams styleParams;
15
+    public List<TopTabParams> topTabParams;
16
+    public String fragmentCreatorClassName;
17
+    public Bundle fragmentCreatorPassProps;
18
+    public boolean animateScreenTransitions;
19
+
20
+    public String tabLabel;
21
+    public Drawable tabIcon;
22
+
23
+    public NavigationParams navigationParams;
24
+
25
+    public boolean hasTopTabs() {
26
+        return topTabParams != null && !topTabParams.isEmpty();
27
+    }
28
+
29
+    public boolean isFragmentScreen() {
30
+        return fragmentCreatorClassName != null;
31
+    }
32
+
33
+    public String getScreenInstanceId() {
34
+        return navigationParams.screenInstanceId;
35
+    }
36
+
37
+    public String getNavigatorId() {
38
+        return navigationParams.navigatorId;
39
+    }
40
+
41
+    public String getNavigatorEventId() {
42
+        return navigationParams.navigatorEventId;
43
+    }
44
+}

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

@@ -0,0 +1,7 @@
1
+package com.reactnativenavigation.params;
2
+
3
+public class SideMenuParams {
4
+    public String screenId;
5
+    public NavigationParams navigationParams;
6
+    public boolean disableOpenGesture;
7
+}

+ 66
- 0
android/app/src/main/java/com/reactnativenavigation/params/StyleParams.java View File

@@ -0,0 +1,66 @@
1
+package com.reactnativenavigation.params;
2
+
3
+import android.support.annotation.ColorInt;
4
+
5
+public class StyleParams {
6
+    public static class Color {
7
+        @ColorInt
8
+        private Integer color = null;
9
+
10
+        public Color() {
11
+            color = null;
12
+        }
13
+
14
+        public Color(Integer color) {
15
+            this.color = color;
16
+        }
17
+
18
+        public boolean hasColor() {
19
+            return color != null;
20
+        }
21
+
22
+        @ColorInt
23
+        public int getColor() {
24
+            if (!hasColor()) {
25
+                throw new RuntimeException("Color undefined");
26
+            }
27
+            return color;
28
+        }
29
+
30
+        public static Color parse(String str) {
31
+            if (str == null) {
32
+                return new Color();
33
+            }
34
+            return new Color(android.graphics.Color.parseColor(str));
35
+        }
36
+    }
37
+
38
+    public Color statusBarColor;
39
+
40
+    public Color topBarColor;
41
+    public boolean topBarHidden;
42
+    public boolean topTabsHidden;
43
+    public boolean drawScreenBelowTopBar;
44
+
45
+    public boolean titleBarHidden;
46
+    public Color titleBarTitleColor;
47
+    public Color titleBarButtonColor;
48
+    public Color titleBarDisabledButtonColor;
49
+    public boolean backButtonHidden;
50
+
51
+    public Color topTabTextColor;
52
+    public Color selectedTopTabTextColor;
53
+    public int selectedTopTabIndicatorHeight;
54
+    public Color selectedTopTabIndicatorColor;
55
+
56
+    public boolean drawScreenAboveBottomTabs;
57
+
58
+    public boolean bottomTabsHidden;
59
+    public boolean bottomTabsHiddenOnScroll;
60
+    public Color bottomTabsColor;
61
+    public Color selectedBottomTabsButtonColor;
62
+    public Color bottomTabsButtonColor;
63
+    public boolean forceTitlesDisplay;
64
+
65
+    public Color navigationBarColor;
66
+}

+ 33
- 0
android/app/src/main/java/com/reactnativenavigation/params/TitleBarButtonParams.java View File

@@ -0,0 +1,33 @@
1
+package com.reactnativenavigation.params;
2
+
3
+import android.graphics.drawable.Drawable;
4
+import android.view.MenuItem;
5
+
6
+public class TitleBarButtonParams {
7
+    public enum ShowAsAction {
8
+        IfRoom(MenuItem.SHOW_AS_ACTION_IF_ROOM),
9
+        Always(MenuItem.SHOW_AS_ACTION_ALWAYS),
10
+        Never(MenuItem.SHOW_AS_ACTION_NEVER),
11
+        WithText(MenuItem.SHOW_AS_ACTION_WITH_TEXT);
12
+
13
+        public final int action;
14
+
15
+        ShowAsAction(int action) {
16
+            this.action = action;
17
+        }
18
+    }
19
+
20
+    public String eventId;
21
+    public String label;
22
+    public Drawable icon;
23
+    public StyleParams.Color color;
24
+    public StyleParams.Color disabledColor;
25
+    public ShowAsAction showAsAction;
26
+    public boolean enabled = true;
27
+
28
+    public void setColorFromScreenStyle(StyleParams.Color titleBarButtonColor) {
29
+        if (!color.hasColor() && titleBarButtonColor.hasColor()) {
30
+            color = titleBarButtonColor;
31
+        }
32
+    }
33
+}

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

@@ -0,0 +1,23 @@
1
+package com.reactnativenavigation.params;
2
+
3
+import com.balysv.materialmenu.MaterialMenuDrawable;
4
+
5
+public class TitleBarLeftButtonParams extends TitleBarButtonParams {
6
+    public MaterialMenuDrawable.IconState iconState;
7
+    public boolean overrideBackPressInJs;
8
+
9
+    public TitleBarLeftButtonParams(TitleBarButtonParams params) {
10
+        icon = params.icon;
11
+        color = params.color;
12
+        eventId = params.eventId;
13
+        enabled = params.enabled;
14
+    }
15
+
16
+    public boolean isBackButton() {
17
+        return eventId.equals("back");
18
+    }
19
+
20
+    public void setOverrideBackPressInJs(boolean overrideBackPressInJs) {
21
+        this.overrideBackPressInJs = overrideBackPressInJs;
22
+    }
23
+}

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

@@ -0,0 +1,7 @@
1
+package com.reactnativenavigation.params;
2
+
3
+public class TopTabParams {
4
+    public String screenId;
5
+    public String title;
6
+    public NavigationParams navigationParams;
7
+}

+ 30
- 0
android/app/src/main/java/com/reactnativenavigation/params/parsers/ActivityParamsParser.java View File

@@ -0,0 +1,30 @@
1
+package com.reactnativenavigation.params.parsers;
2
+
3
+import android.os.Bundle;
4
+
5
+import com.reactnativenavigation.params.ActivityParams;
6
+import com.reactnativenavigation.params.AppStyle;
7
+
8
+public class ActivityParamsParser extends Parser {
9
+    public static ActivityParams parse(Bundle params) {
10
+        ActivityParams result = new ActivityParams();
11
+
12
+        AppStyle.setAppStyle(params);
13
+
14
+        if (hasKey(params, "screen")) {
15
+            result.type = ActivityParams.Type.SingleScreen;
16
+            result.screenParams = ScreenParamsParser.parse(params.getBundle("screen"));
17
+        }
18
+
19
+        if (hasKey(params, "tabs")) {
20
+            result.type = ActivityParams.Type.TabBased;
21
+            result.tabParams = ScreenParamsParser.parseTabs(params.getBundle("tabs"));
22
+        }
23
+
24
+        if (hasKey(params, "sideMenu")) {
25
+            result.sideMenuParams = SideMenuParamsParser.parse(params.getBundle("sideMenu"));
26
+        }
27
+
28
+        return result;
29
+    }
30
+}

+ 23
- 0
android/app/src/main/java/com/reactnativenavigation/params/parsers/Parser.java View File

@@ -0,0 +1,23 @@
1
+package com.reactnativenavigation.params.parsers;
2
+
3
+import android.os.Bundle;
4
+
5
+public class Parser {
6
+
7
+    protected static boolean hasKey(Bundle bundle, String key) {
8
+        return bundle.keySet().contains(key);
9
+    }
10
+
11
+
12
+    protected static void assertKeyExists(Bundle bundle, String key) {
13
+        if (!hasKey(bundle, key)) {
14
+            throw new KeyDoesNotExistsException(key);
15
+        }
16
+    }
17
+
18
+    public static class KeyDoesNotExistsException extends RuntimeException {
19
+        public KeyDoesNotExistsException(String key) {
20
+            super(key);
21
+        }
22
+    }
23
+}

+ 111
- 0
android/app/src/main/java/com/reactnativenavigation/params/parsers/ScreenParamsParser.java View File

@@ -0,0 +1,111 @@
1
+package com.reactnativenavigation.params.parsers;
2
+
3
+import android.graphics.drawable.Drawable;
4
+import android.os.Bundle;
5
+
6
+import com.reactnativenavigation.params.NavigationParams;
7
+import com.reactnativenavigation.params.ScreenParams;
8
+import com.reactnativenavigation.params.TitleBarButtonParams;
9
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
10
+import com.reactnativenavigation.params.TopTabParams;
11
+import com.reactnativenavigation.react.ImageLoader;
12
+
13
+import java.util.ArrayList;
14
+import java.util.Arrays;
15
+import java.util.List;
16
+
17
+public class ScreenParamsParser extends Parser {
18
+    private static final String KEY_TITLE = "title";
19
+    private static final String KEY_SCREEN_ID = "screenId";
20
+    private static final String KEY_NAVIGATION_PARAMS = "navigationParams";
21
+    private static final String KEY_RIGHT_BUTTONS = "rightButtons";
22
+    private static final String KEY_LEFT_BUTTON = "leftButton";
23
+    private static final String KEY_BACK_BUTTON_HIDDEN = "backButtonHidden";
24
+    private static final String STYLE_PARAMS = "styleParams";
25
+    private static final String TOP_TABS = "topTabs";
26
+    private static final String FRAGMENT_CREATOR_CLASS_NAME = "fragmentCreatorClassName";
27
+    private static final String FRAGMENT_CREATOR_PASS_PROPS = "fragmentCreatorPassProps";
28
+    public static final String OVERRIDE_BACK_PRESS = "overrideBackPress";
29
+
30
+    @SuppressWarnings("ConstantConditions")
31
+    public static ScreenParams parse(Bundle params) {
32
+        ScreenParams result = new ScreenParams();
33
+        result.screenId = params.getString(KEY_SCREEN_ID);
34
+        assertKeyExists(params, KEY_NAVIGATION_PARAMS);
35
+        result.navigationParams = new NavigationParams(params.getBundle(KEY_NAVIGATION_PARAMS));
36
+
37
+        result.styleParams = new StyleParamsParser(params.getBundle(STYLE_PARAMS)).parse();
38
+
39
+        result.title = params.getString(KEY_TITLE);
40
+        result.rightButtons = parseRightButton(params);
41
+        result.overrideBackPressInJs = params.getBoolean(OVERRIDE_BACK_PRESS, false);
42
+        result.leftButton = parseLeftButton(params);
43
+
44
+        result.topTabParams = parseTopTabs(params);
45
+
46
+        if (hasKey(params, FRAGMENT_CREATOR_CLASS_NAME)) {
47
+            result.fragmentCreatorClassName = params.getString(FRAGMENT_CREATOR_CLASS_NAME);
48
+            result.fragmentCreatorPassProps = params.getBundle(FRAGMENT_CREATOR_PASS_PROPS);
49
+        }
50
+
51
+        result.tabLabel = getTabLabel(params);
52
+        result.tabIcon = getTabIcon(params);
53
+
54
+        result.animateScreenTransitions = params.getBoolean("animated", true);
55
+
56
+        return result;
57
+    }
58
+
59
+    private static Drawable getTabIcon(Bundle params) {
60
+        Drawable tabIcon = null;
61
+        if (hasKey(params, "icon")) {
62
+            tabIcon = ImageLoader.loadImage(params.getString("icon"));
63
+        }
64
+        return tabIcon;
65
+    }
66
+
67
+    private static String getTabLabel(Bundle params) {
68
+        String tabLabel = null;
69
+        if (hasKey(params, "label")) {
70
+            tabLabel = params.getString("label");
71
+        }
72
+        return tabLabel;
73
+    }
74
+
75
+    private static List<TopTabParams> parseTopTabs(Bundle params) {
76
+        List<TopTabParams> topTabParams = null;
77
+        if (hasKey(params, TOP_TABS)) {
78
+             topTabParams = TopTabParamsParser.parse(params.getBundle(TOP_TABS));
79
+        }
80
+        return topTabParams;
81
+    }
82
+
83
+    private static List<TitleBarButtonParams> parseRightButton(Bundle params) {
84
+        List<TitleBarButtonParams> rightButtons = null;
85
+        if (hasKey(params, KEY_RIGHT_BUTTONS)) {
86
+            rightButtons = new TitleBarButtonParamsParser().parseButtons(params.getBundle(KEY_RIGHT_BUTTONS));
87
+        }
88
+        return rightButtons;
89
+    }
90
+
91
+    private static TitleBarLeftButtonParams parseLeftButton(Bundle params) {
92
+        TitleBarLeftButtonParams leftButton = null;
93
+        if (hasKey(params, KEY_LEFT_BUTTON)) {
94
+            leftButton =  new TitleBarLeftButtonParamsParser().parseSingleButton(params.getBundle(KEY_LEFT_BUTTON));
95
+
96
+            boolean backButtonHidden = params.getBoolean(KEY_BACK_BUTTON_HIDDEN, false);
97
+            if (backButtonHidden && leftButton.isBackButton()) {
98
+                leftButton = null;
99
+            }
100
+        }
101
+        return leftButton;
102
+    }
103
+
104
+    public static List<ScreenParams> parseTabs(Bundle params) {
105
+        ScreenParams result[] = new ScreenParams[params.keySet().size()];
106
+        for (String key : params.keySet()) {
107
+            result[Integer.parseInt(key)] = ScreenParamsParser.parse(params.getBundle(key));
108
+        }
109
+        return new ArrayList<>(Arrays.asList(result));
110
+    }
111
+}

+ 17
- 0
android/app/src/main/java/com/reactnativenavigation/params/parsers/SideMenuParamsParser.java View File

@@ -0,0 +1,17 @@
1
+package com.reactnativenavigation.params.parsers;
2
+
3
+import android.os.Bundle;
4
+
5
+import com.reactnativenavigation.params.NavigationParams;
6
+import com.reactnativenavigation.params.SideMenuParams;
7
+
8
+public class SideMenuParamsParser extends Parser {
9
+
10
+    public static SideMenuParams parse(Bundle sideMenu) {
11
+        SideMenuParams result = new SideMenuParams();
12
+        result.screenId = sideMenu.getString("screenId");
13
+        result.navigationParams = new NavigationParams(sideMenu.getBundle("navigationParams"));
14
+        result.disableOpenGesture = sideMenu.getBoolean("disableOpenGesture", false);
15
+        return result;
16
+    }
17
+}

+ 159
- 0
android/app/src/main/java/com/reactnativenavigation/params/parsers/StyleParamsParser.java View File

@@ -0,0 +1,159 @@
1
+package com.reactnativenavigation.params.parsers;
2
+
3
+import android.graphics.Color;
4
+import android.os.Bundle;
5
+
6
+import com.reactnativenavigation.params.AppStyle;
7
+import com.reactnativenavigation.params.StyleParams;
8
+
9
+public class StyleParamsParser {
10
+    private Bundle params;
11
+
12
+    public StyleParamsParser(Bundle params) {
13
+        this.params = params;
14
+    }
15
+
16
+    public StyleParams parse() {
17
+        StyleParams result = new StyleParams();
18
+        if (params == null) {
19
+            return result;
20
+        }
21
+
22
+        result.statusBarColor = getColor("statusBarColor", getDefaultStatusBarColor());
23
+
24
+        result.topBarColor = getColor("topBarColor", getDefaultTopBarColor());
25
+        result.titleBarHidden = getBoolean("titleBarHidden", getDefaultTopBarHidden());
26
+        result.titleBarTitleColor = getColor("titleBarTitleColor", getDefaultTitleBarColor());
27
+        result.titleBarButtonColor = getColor("titleBarButtonColor", getTitleBarButtonColor());
28
+        result.titleBarDisabledButtonColor = getColor("titleBarDisabledButtonColor", getTitleBarDisabledButtonColor());
29
+        result.backButtonHidden = getBoolean("backButtonHidden", getDefaultBackButtonHidden());
30
+        result.topTabsHidden = getBoolean("topTabsHidden", getDefaultTopTabsHidden());
31
+
32
+        result.topTabTextColor = getColor("topTabTextColor", getDefaultTopTabTextColor());
33
+        result.selectedTopTabTextColor = getColor("selectedTopTabTextColor", getDefaultSelectedTopTabTextColor());
34
+        result.selectedTopTabIndicatorHeight = getInt("selectedTopTabIndicatorHeight", getDefaultSelectedTopTabIndicatorHeight());
35
+        result.selectedTopTabIndicatorColor = getColor("selectedTopTabIndicatorColor", getDefaultSelectedTopTabIndicatorColor());
36
+
37
+        // TODO: Uncomment once we support drawBelowTopBar again
38
+        //result.drawScreenBelowTopBar = params.getBoolean("drawBelowTopBar", isDefaultScreenBelowTopBar());
39
+        result.drawScreenBelowTopBar = true;
40
+
41
+        result.bottomTabsHidden = getBoolean("bottomTabsHidden", getDefaultBottomTabsHidden());
42
+        result.drawScreenAboveBottomTabs = !result.bottomTabsHidden &&
43
+                                           params.getBoolean("drawScreenAboveBottomTabs", getDefaultDrawScreenAboveBottomTabs());
44
+        result.bottomTabsHiddenOnScroll =
45
+                getBoolean("bottomTabsHiddenOnScroll", getDefaultBottomTabsHiddenOnScroll());
46
+        result.bottomTabsColor = getColor("bottomTabsColor", getDefaultBottomTabsColor());
47
+        result.bottomTabsButtonColor = getColor("bottomTabsButtonColor", getDefaultBottomTabsButtonColor());
48
+        result.selectedBottomTabsButtonColor =
49
+                getColor("bottomTabsSelectedButtonColor", getDefaultSelectedBottomTabsButtonColor());
50
+
51
+        result.navigationBarColor = getColor("navigationBarColor", getDefaultNavigationColor());
52
+        result.forceTitlesDisplay = getBoolean("forceTitlesDisplay", getDefaultForceTitlesDisplay());
53
+
54
+        return result;
55
+    }
56
+
57
+    private boolean getDefaultDrawScreenAboveBottomTabs() {
58
+        return AppStyle.appStyle == null || AppStyle.appStyle.drawScreenAboveBottomTabs;
59
+    }
60
+
61
+    private StyleParams.Color getDefaultSelectedTopTabIndicatorColor() {
62
+        return AppStyle.appStyle == null ? new StyleParams.Color() : AppStyle.appStyle.selectedTopTabIndicatorColor;
63
+    }
64
+
65
+    private int getDefaultSelectedTopTabIndicatorHeight() {
66
+        return AppStyle.appStyle == null ? -1 : AppStyle.appStyle.selectedTopTabIndicatorHeight;
67
+    }
68
+
69
+    private StyleParams.Color getDefaultSelectedTopTabTextColor() {
70
+        return AppStyle.appStyle == null ? new StyleParams.Color() : AppStyle.appStyle.selectedTopTabTextColor;
71
+    }
72
+
73
+    private StyleParams.Color getDefaultNavigationColor() {
74
+        return AppStyle.appStyle == null ? new StyleParams.Color() : AppStyle.appStyle.navigationBarColor;
75
+    }
76
+
77
+    private boolean getDefaultForceTitlesDisplay() {
78
+        return AppStyle.appStyle != null && AppStyle.appStyle.forceTitlesDisplay;
79
+    }
80
+
81
+    private StyleParams.Color getDefaultSelectedBottomTabsButtonColor() {
82
+        return AppStyle.appStyle == null ? new StyleParams.Color() : AppStyle.appStyle.selectedBottomTabsButtonColor;
83
+    }
84
+
85
+    private StyleParams.Color getDefaultBottomTabsButtonColor() {
86
+        return AppStyle.appStyle == null ? new StyleParams.Color() : AppStyle.appStyle.bottomTabsButtonColor;
87
+    }
88
+
89
+    private StyleParams.Color getDefaultBottomTabsColor() {
90
+        return AppStyle.appStyle == null ? new StyleParams.Color() : AppStyle.appStyle.bottomTabsColor;
91
+    }
92
+
93
+    private boolean getDefaultBottomTabsHiddenOnScroll() {
94
+        return AppStyle.appStyle != null && AppStyle.appStyle.bottomTabsHiddenOnScroll;
95
+    }
96
+
97
+    private boolean getDefaultBottomTabsHidden() {
98
+        return AppStyle.appStyle != null && AppStyle.appStyle.bottomTabsHidden;
99
+    }
100
+
101
+    private boolean isDefaultScreenBelowTopBar() {
102
+        return AppStyle.appStyle == null || AppStyle.appStyle.drawScreenBelowTopBar;
103
+    }
104
+
105
+    private boolean getDefaultTopTabsHidden() {
106
+        return AppStyle.appStyle != null && AppStyle.appStyle.topTabsHidden;
107
+    }
108
+
109
+    private StyleParams.Color getDefaultTopTabTextColor() {
110
+        return AppStyle.appStyle == null ? new StyleParams.Color() : AppStyle.appStyle.topTabTextColor;
111
+    }
112
+
113
+    private boolean getDefaultBackButtonHidden() {
114
+        return AppStyle.appStyle != null && AppStyle.appStyle.backButtonHidden;
115
+    }
116
+
117
+    private StyleParams.Color getDefaultTitleBarColor() {
118
+        return AppStyle.appStyle == null ? new StyleParams.Color() : AppStyle.appStyle.titleBarTitleColor;
119
+    }
120
+
121
+    private StyleParams.Color getTitleBarButtonColor() {
122
+        return AppStyle.appStyle == null ? new StyleParams.Color() : AppStyle.appStyle.titleBarButtonColor;
123
+    }
124
+
125
+    private StyleParams.Color getTitleBarDisabledButtonColor() {
126
+        return AppStyle.appStyle == null ? new StyleParams.Color(Color.LTGRAY) : AppStyle.appStyle.titleBarDisabledButtonColor;
127
+    }
128
+
129
+    private boolean getDefaultTopBarHidden() {
130
+        return AppStyle.appStyle != null && AppStyle.appStyle.titleBarHidden;
131
+    }
132
+
133
+    private StyleParams.Color getDefaultTopBarColor() {
134
+        return AppStyle.appStyle == null ? new StyleParams.Color() : AppStyle.appStyle.topBarColor;
135
+    }
136
+
137
+    private StyleParams.Color getDefaultStatusBarColor() {
138
+        return AppStyle.appStyle == null ? new StyleParams.Color() : AppStyle.appStyle.statusBarColor;
139
+    }
140
+
141
+    private boolean getBoolean(String key, boolean defaultValue) {
142
+        return params.containsKey(key) ? params.getBoolean(key) : defaultValue;
143
+    }
144
+
145
+    private StyleParams.Color getColor(String key, StyleParams.Color defaultColor) {
146
+        StyleParams.Color color = StyleParams.Color.parse(params.getString(key));
147
+        if (color.hasColor()) {
148
+            return color;
149
+        } else {
150
+            return defaultColor != null && defaultColor.hasColor() ? defaultColor : color;
151
+        }
152
+    }
153
+
154
+    private int getInt(String selectedTopTabIndicatorHeight, int defaultSelectedTopTabIndicatorHeight) {
155
+        return params.containsKey(selectedTopTabIndicatorHeight) ?
156
+                (int) params.getDouble(selectedTopTabIndicatorHeight) :
157
+                defaultSelectedTopTabIndicatorHeight;
158
+    }
159
+}

+ 59
- 0
android/app/src/main/java/com/reactnativenavigation/params/parsers/TitleBarButtonParamsParser.java View File

@@ -0,0 +1,59 @@
1
+package com.reactnativenavigation.params.parsers;
2
+
3
+import android.os.Bundle;
4
+
5
+import com.reactnativenavigation.params.AppStyle;
6
+import com.reactnativenavigation.params.StyleParams;
7
+import com.reactnativenavigation.params.TitleBarButtonParams;
8
+import com.reactnativenavigation.react.ImageLoader;
9
+
10
+import java.util.ArrayList;
11
+import java.util.List;
12
+
13
+public class TitleBarButtonParamsParser extends Parser {
14
+    public List<TitleBarButtonParams> parseButtons(Bundle params) {
15
+        List<TitleBarButtonParams> result = new ArrayList<>();
16
+
17
+        for (String key : params.keySet()) {
18
+            result.add(parseSingleButton(params.getBundle(key)));
19
+        }
20
+        return result;
21
+    }
22
+
23
+    public TitleBarButtonParams parseSingleButton(Bundle bundle) {
24
+        TitleBarButtonParams result = new TitleBarButtonParams();
25
+        result.label = bundle.getString("title");
26
+        if (hasKey(bundle,"icon")) {
27
+            result.icon = ImageLoader.loadImage(bundle.getString("icon"));
28
+        }
29
+        result.color = getColor(bundle, "color", AppStyle.appStyle.titleBarButtonColor);
30
+        result.disabledColor = getColor(bundle, "disabledColor", AppStyle.appStyle.titleBarDisabledButtonColor);
31
+        result.showAsAction = parseShowAsAction(bundle.getString("showAsAction"));
32
+        result.enabled = bundle.getBoolean("enabled", true);
33
+        result.eventId = bundle.getString("id");
34
+        return result;
35
+    }
36
+
37
+    private StyleParams.Color getColor(Bundle bundle, String key, StyleParams.Color defaultColor) {
38
+        StyleParams.Color color = StyleParams.Color.parse(bundle.getString(key));
39
+        return color.hasColor() || defaultColor == null ? color : defaultColor;
40
+    }
41
+
42
+    private static TitleBarButtonParams.ShowAsAction parseShowAsAction(String showAsAction) {
43
+        if (showAsAction == null) {
44
+            return TitleBarButtonParams.ShowAsAction.IfRoom;
45
+        }
46
+
47
+        switch (showAsAction) {
48
+            case "always":
49
+                return TitleBarButtonParams.ShowAsAction.Always;
50
+            case "never":
51
+                return TitleBarButtonParams.ShowAsAction.Never;
52
+            case "withText":
53
+                return TitleBarButtonParams.ShowAsAction.WithText;
54
+            case "ifRoom":
55
+            default:
56
+                return TitleBarButtonParams.ShowAsAction.IfRoom;
57
+        }
58
+    }
59
+}

+ 30
- 0
android/app/src/main/java/com/reactnativenavigation/params/parsers/TitleBarLeftButtonParamsParser.java View File

@@ -0,0 +1,30 @@
1
+package com.reactnativenavigation.params.parsers;
2
+
3
+import android.os.Bundle;
4
+
5
+import com.balysv.materialmenu.MaterialMenuDrawable;
6
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
7
+
8
+public class TitleBarLeftButtonParamsParser extends TitleBarButtonParamsParser {
9
+
10
+    public TitleBarLeftButtonParams parseSingleButton(Bundle params) {
11
+        TitleBarLeftButtonParams leftButtonParams = new TitleBarLeftButtonParams(super.parseSingleButton(params));
12
+        leftButtonParams.iconState = getIconStateFromId(leftButtonParams.eventId);
13
+        return leftButtonParams;
14
+    }
15
+
16
+    private MaterialMenuDrawable.IconState getIconStateFromId(String id) {
17
+        switch (id) {
18
+            case "back":
19
+                return MaterialMenuDrawable.IconState.ARROW;
20
+            case "cancel":
21
+                return MaterialMenuDrawable.IconState.X;
22
+            case "accept":
23
+                return MaterialMenuDrawable.IconState.CHECK;
24
+            case "sideMenu":
25
+                return MaterialMenuDrawable.IconState.BURGER;
26
+            default:
27
+                throw new RuntimeException("Unsupported button type " + id);
28
+        }
29
+    }
30
+}

+ 34
- 0
android/app/src/main/java/com/reactnativenavigation/params/parsers/TopTabParamsParser.java View File

@@ -0,0 +1,34 @@
1
+package com.reactnativenavigation.params.parsers;
2
+
3
+import android.os.Bundle;
4
+import android.support.annotation.NonNull;
5
+
6
+import com.reactnativenavigation.params.NavigationParams;
7
+import com.reactnativenavigation.params.TopTabParams;
8
+
9
+import java.util.ArrayList;
10
+import java.util.List;
11
+
12
+public class TopTabParamsParser extends Parser {
13
+    private static final String KEY_SCREEN_ID = "screenId";
14
+    private static final String KEY_TITLE = "title";
15
+    private static final String NAVIGATION_PARAMS = "navigationParams";
16
+
17
+    @SuppressWarnings("ConstantConditions")
18
+    public static List<TopTabParams> parse(Bundle params) {
19
+        List<TopTabParams> result = new ArrayList<>();
20
+        for (String key : params.keySet()) {
21
+            result.add(parseItem(params.getBundle(key)));
22
+        }
23
+        return result;
24
+    }
25
+
26
+    @NonNull
27
+    private static TopTabParams parseItem(Bundle params) {
28
+        TopTabParams result = new TopTabParams();
29
+        result.screenId = params.getString(KEY_SCREEN_ID);
30
+        result.title = params.getString(KEY_TITLE);
31
+        result.navigationParams = new NavigationParams(params.getBundle(NAVIGATION_PARAMS));
32
+        return result;
33
+    }
34
+}

+ 16
- 0
android/app/src/main/java/com/reactnativenavigation/react/ImageLoader.java View File

@@ -0,0 +1,16 @@
1
+package com.reactnativenavigation.react;
2
+
3
+import android.graphics.drawable.Drawable;
4
+
5
+import com.reactnativenavigation.NavigationApplication;
6
+
7
+public class ImageLoader {
8
+
9
+    public static Drawable loadImage(String iconSource) {
10
+        if (NavigationApplication.instance.isDebug()) {
11
+            return JsDevImageLoader.loadIcon(iconSource);
12
+        } else {
13
+            return ResourceDrawableIdHelper.instance.getResourceDrawable(NavigationApplication.instance, iconSource);
14
+        }
15
+    }
16
+}

+ 37
- 0
android/app/src/main/java/com/reactnativenavigation/react/JsDevImageLoader.java View File

@@ -0,0 +1,37 @@
1
+package com.reactnativenavigation.react;
2
+
3
+import android.graphics.Bitmap;
4
+import android.graphics.BitmapFactory;
5
+import android.graphics.drawable.BitmapDrawable;
6
+import android.graphics.drawable.Drawable;
7
+import android.os.StrictMode;
8
+import android.support.annotation.NonNull;
9
+
10
+import com.reactnativenavigation.NavigationApplication;
11
+
12
+import java.io.IOException;
13
+import java.net.URL;
14
+
15
+public class JsDevImageLoader {
16
+
17
+    public static Drawable loadIcon(String iconDevUri) {
18
+        try {
19
+            StrictMode.ThreadPolicy threadPolicy = StrictMode.getThreadPolicy();
20
+            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitNetwork().build());
21
+
22
+            Drawable drawable = tryLoadIcon(iconDevUri);
23
+
24
+            StrictMode.setThreadPolicy(threadPolicy);
25
+            return drawable;
26
+        } catch (Exception e) {
27
+            throw new RuntimeException(iconDevUri, e);
28
+        }
29
+    }
30
+
31
+    @NonNull
32
+    private static Drawable tryLoadIcon(String iconDevUri) throws IOException {
33
+        URL url = new URL(iconDevUri);
34
+        Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());
35
+        return new BitmapDrawable(NavigationApplication.instance.getResources(), bitmap);
36
+    }
37
+}

+ 48
- 0
android/app/src/main/java/com/reactnativenavigation/react/JsDevReloadHandler.java View File

@@ -0,0 +1,48 @@
1
+package com.reactnativenavigation.react;
2
+
3
+import android.view.KeyEvent;
4
+import android.view.View;
5
+import android.widget.EditText;
6
+
7
+import com.facebook.react.ReactInstanceManager;
8
+import com.reactnativenavigation.NavigationApplication;
9
+
10
+public class JsDevReloadHandler {
11
+
12
+    private static boolean shouldRefreshOnRR = false;
13
+
14
+    //TODO yuck.
15
+    public static boolean onKeyUp(View currentFocus, int keyCode) {
16
+        ReactInstanceManager reactInstanceManager = NavigationApplication
17
+                .instance
18
+                .getReactGateway()
19
+                .getReactInstanceManager();
20
+
21
+        if (reactInstanceManager != null &&
22
+                reactInstanceManager.getDevSupportManager().getDevSupportEnabled()) {
23
+            if (keyCode == KeyEvent.KEYCODE_MENU) {
24
+                reactInstanceManager.showDevOptionsDialog();
25
+                return true;
26
+            }
27
+            if (keyCode == KeyEvent.KEYCODE_R && !(currentFocus instanceof EditText)) {
28
+                // Enable double-tap-R-to-reload
29
+                if (shouldRefreshOnRR) {
30
+                    reactInstanceManager.getDevSupportManager().handleReloadJS();
31
+                    shouldRefreshOnRR = false;
32
+                    return true;
33
+                } else {
34
+                    shouldRefreshOnRR = true;
35
+                    NavigationApplication.instance.runOnMainThread(
36
+                            new Runnable() {
37
+                                @Override
38
+                                public void run() {
39
+                                    shouldRefreshOnRR = false;
40
+                                }
41
+                            },
42
+                            500);
43
+                }
44
+            }
45
+        }
46
+        return false;
47
+    }
48
+}

+ 69
- 0
android/app/src/main/java/com/reactnativenavigation/react/JsDevReloadListenerReplacer.java View File

@@ -0,0 +1,69 @@
1
+package com.reactnativenavigation.react;
2
+
3
+import com.facebook.react.ReactInstanceManager;
4
+import com.facebook.react.bridge.JavaJSExecutor;
5
+import com.facebook.react.devsupport.DevSupportManager;
6
+import com.facebook.react.devsupport.ReactInstanceDevCommandsHandler;
7
+import com.reactnativenavigation.utils.ReflectionUtils;
8
+
9
+public class JsDevReloadListenerReplacer {
10
+    private final ReactInstanceManager reactInstanceManager;
11
+    private final Listener listener;
12
+
13
+    public interface Listener {
14
+        void onJsDevReload();
15
+    }
16
+
17
+    public JsDevReloadListenerReplacer(ReactInstanceManager reactInstanceManager, Listener listener) {
18
+        this.reactInstanceManager = reactInstanceManager;
19
+        this.listener = listener;
20
+    }
21
+
22
+    public void replace() {
23
+        ReactInstanceDevCommandsHandler originalHandler = getOriginalHandler();
24
+        DevCommandsHandlerProxy proxy = new DevCommandsHandlerProxy(originalHandler, listener);
25
+        replaceInReactInstanceManager(proxy);
26
+        replaceInDevSupportManager(proxy);
27
+    }
28
+
29
+    private void replaceInDevSupportManager(DevCommandsHandlerProxy proxy) {
30
+        DevSupportManager devSupportManager = (DevSupportManager)
31
+                ReflectionUtils.getDeclaredField(reactInstanceManager, "mDevSupportManager");
32
+        ReflectionUtils.setField(devSupportManager, "mReactInstanceCommandsHandler", proxy);
33
+    }
34
+
35
+    private ReactInstanceDevCommandsHandler getOriginalHandler() {
36
+        return (ReactInstanceDevCommandsHandler) ReflectionUtils.getDeclaredField(reactInstanceManager, "mDevInterface");
37
+    }
38
+
39
+    private void replaceInReactInstanceManager(DevCommandsHandlerProxy proxy) {
40
+        ReflectionUtils.setField(reactInstanceManager, "mDevInterface", proxy);
41
+    }
42
+
43
+    private static class DevCommandsHandlerProxy implements ReactInstanceDevCommandsHandler {
44
+        private ReactInstanceDevCommandsHandler originalReactHandler;
45
+        private final Listener listener;
46
+
47
+        public DevCommandsHandlerProxy(ReactInstanceDevCommandsHandler originalReactHandler, Listener listener) {
48
+            this.originalReactHandler = originalReactHandler;
49
+            this.listener = listener;
50
+        }
51
+
52
+        @Override
53
+        public void onReloadWithJSDebugger(JavaJSExecutor.Factory proxyExecutorFactory) {
54
+            listener.onJsDevReload();
55
+            originalReactHandler.onReloadWithJSDebugger(proxyExecutorFactory);
56
+        }
57
+
58
+        @Override
59
+        public void onJSBundleLoadedFromServer() {
60
+            listener.onJsDevReload();
61
+            originalReactHandler.onJSBundleLoadedFromServer();
62
+        }
63
+
64
+        @Override
65
+        public void toggleElementInspector() {
66
+            originalReactHandler.toggleElementInspector();
67
+        }
68
+    }
69
+}

+ 143
- 0
android/app/src/main/java/com/reactnativenavigation/react/NavigationReactGateway.java View File

@@ -0,0 +1,143 @@
1
+package com.reactnativenavigation.react;
2
+
3
+import android.app.Activity;
4
+import android.content.Intent;
5
+
6
+import com.facebook.react.LifecycleState;
7
+import com.facebook.react.ReactInstanceManager;
8
+import com.facebook.react.ReactPackage;
9
+import com.facebook.react.bridge.ReactContext;
10
+import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
11
+import com.facebook.react.shell.MainReactPackage;
12
+import com.reactnativenavigation.NavigationApplication;
13
+import com.reactnativenavigation.bridge.NavigationReactEventEmitter;
14
+import com.reactnativenavigation.bridge.NavigationReactPackage;
15
+
16
+import java.util.ArrayList;
17
+import java.util.List;
18
+
19
+public class NavigationReactGateway implements ReactGateway, ReactInstanceManager.ReactInstanceEventListener {
20
+
21
+    private OnJsDevReloadListener onJsDevReloadListener;
22
+    private ReactInstanceManager reactInstanceManager;
23
+    private NavigationReactEventEmitter reactEventEmitter;
24
+
25
+    public NavigationReactGateway() {
26
+        reactInstanceManager = createReactInstanceManager();
27
+    }
28
+
29
+    @Override
30
+    public void startReactContextOnceInBackgroundAndExecuteJS() {
31
+        if (reactInstanceManager == null) {
32
+            reactInstanceManager = createReactInstanceManager();
33
+        }
34
+
35
+        if (!reactInstanceManager.hasStartedCreatingInitialContext()) {
36
+            reactInstanceManager.createReactContextInBackground();
37
+        }
38
+    }
39
+
40
+    public boolean isInitialized() {
41
+        return reactInstanceManager != null && reactInstanceManager.getCurrentReactContext() != null;
42
+    }
43
+
44
+    public ReactContext getReactContext() {
45
+        return reactInstanceManager.getCurrentReactContext();
46
+    }
47
+
48
+    public NavigationReactEventEmitter getReactEventEmitter() {
49
+        return reactEventEmitter;
50
+    }
51
+
52
+    public ReactInstanceManager getReactInstanceManager() {
53
+        return reactInstanceManager;
54
+    }
55
+
56
+    public void onBackPressed() {
57
+        reactInstanceManager.onBackPressed();
58
+    }
59
+
60
+    public void onDestroyApp() {
61
+        reactInstanceManager.onHostDestroy();
62
+        reactInstanceManager.destroy();
63
+        reactInstanceManager.removeReactInstanceEventListener(this);
64
+        reactInstanceManager = null;
65
+    }
66
+
67
+    public void onPauseActivity() {
68
+        reactInstanceManager.onHostPause();
69
+        onJsDevReloadListener = null;
70
+    }
71
+
72
+    public void onResumeActivity(Activity activity, DefaultHardwareBackBtnHandler defaultHardwareBackBtnHandler, OnJsDevReloadListener onJsDevReloadListener) {
73
+        this.onJsDevReloadListener = onJsDevReloadListener;
74
+        reactInstanceManager.onHostResume(activity, defaultHardwareBackBtnHandler);
75
+    }
76
+
77
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
78
+        reactInstanceManager.onActivityResult(requestCode, resultCode, data);
79
+    }
80
+
81
+    private void replaceJsDevReloadListener(ReactInstanceManager manager) {
82
+        new JsDevReloadListenerReplacer(manager, new JsDevReloadListenerReplacer.Listener() {
83
+            @Override
84
+            public void onJsDevReload() {
85
+                if (onJsDevReloadListener != null)
86
+                    onJsDevReloadListener.onJsDevReload();
87
+            }
88
+        }).replace();
89
+    }
90
+
91
+    private ReactInstanceManager createReactInstanceManager() {
92
+        ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
93
+                .setApplication(NavigationApplication.instance)
94
+                .setJSMainModuleName(NavigationApplication.instance.getJsEntryFileName())
95
+                .setBundleAssetName(NavigationApplication.instance.getBundleAssetName())
96
+                .setUseDeveloperSupport(NavigationApplication.instance.isDebug())
97
+                .setInitialLifecycleState(LifecycleState.BEFORE_RESUME);
98
+
99
+        for (ReactPackage reactPackage : createReactPackages()) {
100
+            builder.addPackage(reactPackage);
101
+        }
102
+
103
+        ReactInstanceManager manager = builder.build();
104
+
105
+        if (NavigationApplication.instance.isDebug()) {
106
+            replaceJsDevReloadListener(manager);
107
+        }
108
+
109
+        manager.addReactInstanceEventListener(this);
110
+
111
+        return manager;
112
+    }
113
+
114
+    private List<ReactPackage> createReactPackages() {
115
+        List<ReactPackage> list = new ArrayList<>();
116
+        list.add(new MainReactPackage());
117
+        list.add(new NavigationReactPackage());
118
+        addAdditionalReactPackagesIfNeeded(list);
119
+        return list;
120
+    }
121
+
122
+    private void addAdditionalReactPackagesIfNeeded(List<ReactPackage> list) {
123
+        List<ReactPackage> additionalReactPackages = NavigationApplication.instance.createAdditionalReactPackages();
124
+        if (additionalReactPackages == null) {
125
+            return;
126
+        }
127
+
128
+        for (ReactPackage reactPackage : additionalReactPackages) {
129
+            if (reactPackage instanceof MainReactPackage)
130
+                throw new RuntimeException("Do not create a new MainReactPackage. This is created for you.");
131
+            if (reactPackage instanceof NavigationReactPackage)
132
+                throw new RuntimeException("Do not create a new NavigationReactPackage. This is created for you.");
133
+        }
134
+
135
+        list.addAll(additionalReactPackages);
136
+    }
137
+
138
+    @Override
139
+    public void onReactContextInitialized(ReactContext context) {
140
+        reactEventEmitter = new NavigationReactEventEmitter(context);
141
+        NavigationApplication.instance.onReactInitialized(context);
142
+    }
143
+}

+ 36
- 0
android/app/src/main/java/com/reactnativenavigation/react/ReactGateway.java View File

@@ -0,0 +1,36 @@
1
+package com.reactnativenavigation.react;
2
+
3
+import android.app.Activity;
4
+import android.content.Intent;
5
+
6
+import com.facebook.react.ReactInstanceManager;
7
+import com.facebook.react.bridge.ReactContext;
8
+import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
9
+import com.reactnativenavigation.bridge.NavigationReactEventEmitter;
10
+
11
+public interface ReactGateway {
12
+
13
+    interface OnJsDevReloadListener {
14
+        void onJsDevReload();
15
+    }
16
+
17
+    void startReactContextOnceInBackgroundAndExecuteJS();
18
+
19
+    boolean isInitialized();
20
+
21
+    ReactContext getReactContext();
22
+
23
+    NavigationReactEventEmitter getReactEventEmitter();
24
+
25
+    ReactInstanceManager getReactInstanceManager();
26
+
27
+    void onResumeActivity(Activity activity, DefaultHardwareBackBtnHandler defaultHardwareBackBtnHandler, OnJsDevReloadListener onJsDevReloadListener);
28
+
29
+    void onPauseActivity();
30
+
31
+    void onDestroyApp();
32
+
33
+    void onBackPressed();
34
+
35
+    void onActivityResult(int requestCode, int resultCode, Intent data);
36
+}

+ 26
- 0
android/app/src/main/java/com/reactnativenavigation/react/ReactViewHacks.java View File

@@ -0,0 +1,26 @@
1
+package com.reactnativenavigation.react;
2
+
3
+import com.facebook.react.ReactRootView;
4
+import com.reactnativenavigation.utils.ReflectionUtils;
5
+
6
+public class ReactViewHacks {
7
+
8
+    public static void preventUnmountOnDetachedFromWindow(ReactRootView view) {
9
+        ReflectionUtils.setField(view, "mAttachScheduled", true);
10
+    }
11
+
12
+    /**
13
+     * Side effect: prevents JS components constructor from being called
14
+     */
15
+    public static void ensureUnmountOnDetachedFromWindow(ReactRootView view) {
16
+        ReflectionUtils.setField(view, "mAttachScheduled", false);
17
+    }
18
+
19
+    /**
20
+     * Side effect: ensures unmount will be called
21
+     */
22
+    public static void preventMountAfterReattachedToWindow(ReactRootView view) {
23
+        ReflectionUtils.setField(view, "mAttachScheduled", false);
24
+    }
25
+
26
+}

+ 26
- 0
android/app/src/main/java/com/reactnativenavigation/react/RedboxPermission.java View File

@@ -0,0 +1,26 @@
1
+package com.reactnativenavigation.react;
2
+
3
+import android.content.Context;
4
+import android.content.Intent;
5
+import android.os.Build;
6
+import android.provider.Settings;
7
+import android.util.Log;
8
+import android.widget.Toast;
9
+
10
+import com.facebook.react.common.ReactConstants;
11
+import com.reactnativenavigation.NavigationApplication;
12
+
13
+public class RedboxPermission {
14
+
15
+    public static void permissionToShowRedboxIfNeeded(Context context) {
16
+        if (NavigationApplication.instance.isDebug() &&
17
+                Build.VERSION.SDK_INT >= 23 &&
18
+                !Settings.canDrawOverlays(context)) {
19
+            Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
20
+            context.startActivity(serviceIntent);
21
+            String msg = "Overlay permissions needs to be granted in order for react native apps to run in dev mode";
22
+            Log.w(ReactConstants.TAG, msg);
23
+            Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
24
+        }
25
+    }
26
+}

android/app/src/main/java/com/reactnativenavigation/utils/ResourceDrawableIdHelper.java → android/app/src/main/java/com/reactnativenavigation/react/ResourceDrawableIdHelper.java View File

@@ -1,4 +1,4 @@
1
-package com.reactnativenavigation.utils;// Copyright 2004-present Facebook. All Rights Reserved.
1
+package com.reactnativenavigation.react;// Copyright 2004-present Facebook. All Rights Reserved.
2 2
 
3 3
 import android.content.Context;
4 4
 import android.graphics.drawable.Drawable;
@@ -12,9 +12,11 @@ import java.util.Map;
12 12
 import javax.annotation.Nullable;
13 13
 
14 14
 /**
15
- * Helper class for obtaining information about local images.
15
+ * Direct copy paste from react-native, because they made that class package scope. -_-"
16
+ * Can be deleted in react-native ^0.29
16 17
  */
17 18
 public class ResourceDrawableIdHelper {
19
+    public static final ResourceDrawableIdHelper instance = new ResourceDrawableIdHelper();
18 20
 
19 21
     private Map<String, Integer> mResourceDrawableIdMap;
20 22
 

+ 60
- 0
android/app/src/main/java/com/reactnativenavigation/screens/ContentViewPagerAdapter.java View File

@@ -0,0 +1,60 @@
1
+package com.reactnativenavigation.screens;
2
+
3
+import android.support.v4.view.PagerAdapter;
4
+import android.view.View;
5
+import android.view.ViewGroup;
6
+
7
+import com.facebook.react.bridge.Arguments;
8
+import com.facebook.react.bridge.WritableMap;
9
+import com.reactnativenavigation.NavigationApplication;
10
+import com.reactnativenavigation.params.TopTabParams;
11
+import com.reactnativenavigation.views.ContentView;
12
+
13
+import java.util.List;
14
+
15
+public class ContentViewPagerAdapter extends PagerAdapter {
16
+
17
+    private List<ContentView> contentViews;
18
+    private List<TopTabParams> topTabParams;
19
+    private int currentPosition = 0;
20
+
21
+    public ContentViewPagerAdapter(List<ContentView> contentViews, List<TopTabParams> topTabParams) {
22
+        this.contentViews = contentViews;
23
+        this.topTabParams = topTabParams;
24
+    }
25
+
26
+    @Override
27
+    public Object instantiateItem(ViewGroup container, int position) {
28
+        return contentViews.get(position);
29
+    }
30
+
31
+    @Override
32
+    public void setPrimaryItem(ViewGroup container, int position, Object object) {
33
+        super.setPrimaryItem(container, position, object);
34
+        if (position != currentPosition) {
35
+            currentPosition = position;
36
+            sendPageChangeEvent();
37
+        }
38
+    }
39
+
40
+    private void sendPageChangeEvent() {
41
+        WritableMap data = Arguments.createMap();
42
+        String navigatorEventId = contentViews.get(currentPosition).getNavigatorEventId();
43
+        NavigationApplication.instance.sendNavigatorEvent("tabSelected", navigatorEventId, data);
44
+    }
45
+
46
+    @Override
47
+    public int getCount() {
48
+        return contentViews.size();
49
+    }
50
+
51
+    @Override
52
+    public boolean isViewFromObject(View view, Object object) {
53
+        return view == object;
54
+    }
55
+
56
+    @Override
57
+    public CharSequence getPageTitle(int position) {
58
+        return topTabParams.get(position).title;
59
+    }
60
+}

+ 135
- 0
android/app/src/main/java/com/reactnativenavigation/screens/FragmentScreen.java View File

@@ -0,0 +1,135 @@
1
+package com.reactnativenavigation.screens;
2
+
3
+import android.app.Fragment;
4
+import android.app.FragmentManager;
5
+import android.app.FragmentTransaction;
6
+import android.os.Bundle;
7
+import android.support.annotation.Nullable;
8
+import android.support.v7.app.AppCompatActivity;
9
+import android.widget.FrameLayout;
10
+
11
+import com.reactnativenavigation.R;
12
+import com.reactnativenavigation.params.ScreenParams;
13
+import com.reactnativenavigation.utils.ViewUtils;
14
+import com.reactnativenavigation.views.ContentView;
15
+import com.reactnativenavigation.views.LeftButtonOnClickListener;
16
+
17
+import java.lang.reflect.InvocationTargetException;
18
+import java.lang.reflect.Method;
19
+
20
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
21
+
22
+@SuppressWarnings("ResourceType")
23
+public class FragmentScreen extends Screen {
24
+
25
+    private static final String CONTRACT_GET_FRAGMENT = "getFragment";
26
+    private static final String CONTRACT_GET_SUPPORT_FRAGMENT = "getSupportFragment";
27
+    private FrameLayout content;
28
+
29
+    public FragmentScreen(AppCompatActivity activity, ScreenParams screenParams, LeftButtonOnClickListener leftButtonOnClickListener) {
30
+        super(activity, screenParams, leftButtonOnClickListener);
31
+    }
32
+
33
+    @Override
34
+    protected void createContent() {
35
+        content = new FrameLayout(getContext());
36
+        content.setId(R.id.fragment_screen_content);
37
+        addContent();
38
+        addFragment();
39
+    }
40
+
41
+    private void addContent() {
42
+        ContentView contentView = new ContentView(getContext(),
43
+                screenParams.screenId,
44
+                screenParams.navigationParams);
45
+        addView(contentView, 0, 0);
46
+        LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
47
+        if (screenParams.styleParams.drawScreenBelowTopBar) {
48
+            params.addRule(BELOW, topBar.getId());
49
+        }
50
+        addView(content, params);
51
+    }
52
+
53
+
54
+    private void addFragment() {
55
+        try {
56
+            Fragment fragment = tryGetFragment();
57
+            if (fragment != null) {
58
+                addFragment(fragment);
59
+                return;
60
+            }
61
+
62
+            android.support.v4.app.Fragment supportFragment = tryGetSupportFragment();
63
+            if (supportFragment != null) {
64
+                addSupportFragment(supportFragment);
65
+                return;
66
+            }
67
+            throw new RuntimeException("must provide public static method " + CONTRACT_GET_FRAGMENT + " or " + CONTRACT_GET_SUPPORT_FRAGMENT);
68
+        } catch (Exception e) {
69
+            throw new RuntimeException(e);
70
+        }
71
+    }
72
+
73
+    private void addFragment(Fragment fragment) {
74
+        FragmentManager fm = activity.getFragmentManager();
75
+        FragmentTransaction transaction = fm.beginTransaction();
76
+        transaction.add(R.id.fragment_screen_content, fragment);
77
+        transaction.commit();
78
+    }
79
+
80
+    private void addSupportFragment(android.support.v4.app.Fragment supportFragment) {
81
+        android.support.v4.app.FragmentManager fm = activity.getSupportFragmentManager();
82
+        android.support.v4.app.FragmentTransaction transaction = fm.beginTransaction();
83
+        transaction.add(R.id.fragment_screen_content, supportFragment);
84
+        transaction.commit();
85
+    }
86
+
87
+    @Nullable
88
+    private Fragment tryGetFragment() throws ClassNotFoundException, IllegalAccessException, InvocationTargetException {
89
+        try {
90
+            String className = screenParams.fragmentCreatorClassName;
91
+            Class<?> fragmentCreatorClass = Class.forName(className);
92
+            Method method = fragmentCreatorClass.getMethod(CONTRACT_GET_FRAGMENT, Bundle.class);
93
+            return (android.app.Fragment) method.invoke(null, screenParams.fragmentCreatorPassProps);
94
+        } catch (NoSuchMethodException noSuchMethod) {
95
+            return null;
96
+        }
97
+    }
98
+
99
+    @Nullable
100
+    private android.support.v4.app.Fragment tryGetSupportFragment() throws ClassNotFoundException, IllegalAccessException, InvocationTargetException {
101
+        try {
102
+            String className = screenParams.fragmentCreatorClassName;
103
+            Class<?> fragmentCreatorClass = Class.forName(className);
104
+            Method method = fragmentCreatorClass.getMethod(CONTRACT_GET_SUPPORT_FRAGMENT, Bundle.class);
105
+            return (android.support.v4.app.Fragment) method.invoke(null, screenParams.fragmentCreatorPassProps);
106
+        } catch (NoSuchMethodException noSuchMethod) {
107
+            return null;
108
+        }
109
+    }
110
+
111
+    @Override
112
+    public void ensureUnmountOnDetachedFromWindow() {
113
+        // nothing
114
+    }
115
+
116
+    @Override
117
+    public void preventUnmountOnDetachedFromWindow() {
118
+        // nothing
119
+    }
120
+
121
+    @Override
122
+    public void preventMountAfterReattachedToWindow() {
123
+        // nothing
124
+    }
125
+
126
+    @Override
127
+    public void setOnDisplayListener(final OnDisplayListener onContentViewDisplayedListener) {
128
+        ViewUtils.runOnPreDraw(content, new Runnable() {
129
+            @Override
130
+            public void run() {
131
+                onContentViewDisplayedListener.onDisplay();
132
+            }
133
+        });
134
+    }
135
+}

+ 188
- 0
android/app/src/main/java/com/reactnativenavigation/screens/Screen.java View File

@@ -0,0 +1,188 @@
1
+package com.reactnativenavigation.screens;
2
+
3
+import android.annotation.TargetApi;
4
+import android.app.Activity;
5
+import android.graphics.Color;
6
+import android.os.Build;
7
+import android.support.v7.app.AppCompatActivity;
8
+import android.view.Window;
9
+import android.widget.RelativeLayout;
10
+
11
+import com.reactnativenavigation.animation.VisibilityAnimator;
12
+import com.reactnativenavigation.params.ScreenParams;
13
+import com.reactnativenavigation.params.StyleParams;
14
+import com.reactnativenavigation.params.TitleBarButtonParams;
15
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
16
+import com.reactnativenavigation.utils.ViewUtils;
17
+import com.reactnativenavigation.views.LeftButtonOnClickListener;
18
+import com.reactnativenavigation.views.TopBar;
19
+
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
+
25
+public abstract class Screen extends RelativeLayout {
26
+
27
+    public interface OnDisplayListener {
28
+        void onDisplay();
29
+    }
30
+
31
+    protected final AppCompatActivity activity;
32
+    protected final ScreenParams screenParams;
33
+    protected TopBar topBar;
34
+    private final LeftButtonOnClickListener leftButtonOnClickListener;
35
+    private VisibilityAnimator topBarVisibilityAnimator;
36
+    private ScreenAnimator screenAnimator;
37
+    private final StyleParams styleParams;
38
+
39
+    public Screen(AppCompatActivity activity, ScreenParams screenParams, LeftButtonOnClickListener leftButtonOnClickListener) {
40
+        super(activity);
41
+        this.activity = activity;
42
+        this.screenParams = screenParams;
43
+        styleParams = screenParams.styleParams;
44
+        this.leftButtonOnClickListener = leftButtonOnClickListener;
45
+        screenAnimator = new ScreenAnimator(this);
46
+        createViews();
47
+    }
48
+
49
+    public void setStyle() {
50
+        setStatusBarColor(styleParams.statusBarColor);
51
+        setNavigationBarColor(styleParams.navigationBarColor);
52
+        topBar.setStyle(styleParams);
53
+    }
54
+
55
+    private void createViews() {
56
+        createTopBar();
57
+        createTitleBar();
58
+        createContent();
59
+    }
60
+
61
+    protected abstract void createContent();
62
+
63
+    private void createTitleBar() {
64
+        addTitleBarButtons();
65
+        topBar.setTitle(screenParams.title);
66
+    }
67
+
68
+    private void addTitleBarButtons() {
69
+        setButtonColorFromScreen(screenParams.rightButtons);
70
+        if (screenParams.leftButton != null) {
71
+            screenParams.leftButton.setColorFromScreenStyle(screenParams.styleParams.titleBarButtonColor);
72
+        }
73
+        topBar.addTitleBarAndSetButtons(screenParams.rightButtons,
74
+                screenParams.leftButton,
75
+                leftButtonOnClickListener,
76
+                screenParams.getNavigatorEventId(),
77
+                screenParams.overrideBackPressInJs);
78
+    }
79
+
80
+    private void createTopBar() {
81
+        topBar = new TopBar(getContext());
82
+        createTopBarVisibilityAnimator();
83
+        addView(topBar, new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
84
+    }
85
+
86
+    private void createTopBarVisibilityAnimator() {
87
+        ViewUtils.runOnPreDraw(topBar, new Runnable() {
88
+            @Override
89
+            public void run() {
90
+                if (topBarVisibilityAnimator == null) {
91
+                    topBarVisibilityAnimator = new VisibilityAnimator(topBar,
92
+                            VisibilityAnimator.HideDirection.Up,
93
+                            topBar.getHeight());
94
+                }
95
+            }
96
+        });
97
+    }
98
+
99
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
100
+    private void setStatusBarColor(StyleParams.Color statusBarColor) {
101
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
102
+
103
+        final Activity context = (Activity) getContext();
104
+        final Window window = context.getWindow();
105
+        if (statusBarColor.hasColor()) {
106
+            window.setStatusBarColor(statusBarColor.getColor());
107
+        } else {
108
+            window.setStatusBarColor(Color.BLACK);
109
+        }
110
+    }
111
+
112
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
113
+    public void setNavigationBarColor(StyleParams.Color navigationBarColor) {
114
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
115
+
116
+        final Activity context = (Activity) getContext();
117
+        final Window window = context.getWindow();
118
+        if (navigationBarColor.hasColor()) {
119
+            window.setNavigationBarColor(navigationBarColor.getColor());
120
+        } else {
121
+            window.setNavigationBarColor(Color.BLACK);
122
+        }
123
+    }
124
+
125
+    public abstract void ensureUnmountOnDetachedFromWindow();
126
+
127
+    public abstract void preventUnmountOnDetachedFromWindow();
128
+
129
+    public abstract void preventMountAfterReattachedToWindow();
130
+
131
+    public String getScreenInstanceId() {
132
+        return screenParams.getScreenInstanceId();
133
+    }
134
+
135
+    public void setTopBarVisible(boolean visible, boolean animate) {
136
+        topBarVisibilityAnimator.setVisible(visible, animate);
137
+    }
138
+
139
+    public void setTitleBarTitle(String title) {
140
+        topBar.setTitle(title);
141
+    }
142
+
143
+    public void setTitleBarRightButtons(String navigatorEventId, List<TitleBarButtonParams> titleBarButtons) {
144
+        setButtonColorFromScreen(titleBarButtons);
145
+        topBar.setTitleBarRightButtons(navigatorEventId, titleBarButtons);
146
+    }
147
+
148
+    public void setTitleBarLeftButton(String navigatorEventId, LeftButtonOnClickListener backButtonListener,
149
+                                      TitleBarLeftButtonParams titleBarLeftButtonParams) {
150
+        topBar.setTitleBarLeftButton(navigatorEventId,
151
+                backButtonListener,
152
+                titleBarLeftButtonParams,
153
+                screenParams.overrideBackPressInJs);
154
+    }
155
+
156
+    public StyleParams getStyleParams() {
157
+        return screenParams.styleParams;
158
+    }
159
+
160
+    private void setButtonColorFromScreen(List<TitleBarButtonParams> titleBarButtonParams) {
161
+        if (titleBarButtonParams == null) {
162
+            return;
163
+        }
164
+
165
+        for (TitleBarButtonParams titleBarButtonParam : titleBarButtonParams) {
166
+            titleBarButtonParam.setColorFromScreenStyle(screenParams.styleParams.titleBarButtonColor);
167
+        }
168
+    }
169
+
170
+    public abstract void setOnDisplayListener(OnDisplayListener onContentViewDisplayedListener);
171
+
172
+    public void show() {
173
+        screenAnimator.show(screenParams.animateScreenTransitions);
174
+    }
175
+
176
+    public void show(boolean animated) {
177
+        screenAnimator.show(animated);
178
+    }
179
+
180
+    public void show(boolean animated, Runnable onAnimationEnd) {
181
+        setStyle();
182
+        screenAnimator.show(animated, onAnimationEnd);
183
+    }
184
+
185
+    public void hide(boolean animated, Runnable onAnimatedEnd) {
186
+        screenAnimator.hide(animated, onAnimatedEnd);
187
+    }
188
+}

+ 99
- 0
android/app/src/main/java/com/reactnativenavigation/screens/ScreenAnimator.java View File

@@ -0,0 +1,99 @@
1
+package com.reactnativenavigation.screens;
2
+
3
+import android.animation.Animator;
4
+import android.animation.AnimatorListenerAdapter;
5
+import android.animation.AnimatorSet;
6
+import android.animation.ObjectAnimator;
7
+import android.view.View;
8
+import android.view.animation.AccelerateInterpolator;
9
+import android.view.animation.DecelerateInterpolator;
10
+import android.view.animation.LinearInterpolator;
11
+
12
+import com.reactnativenavigation.NavigationApplication;
13
+import com.reactnativenavigation.utils.ViewUtils;
14
+
15
+import javax.annotation.Nullable;
16
+
17
+public class ScreenAnimator {
18
+    private final float translationY;
19
+    private Screen screen;
20
+
21
+    public ScreenAnimator(Screen screen) {
22
+        this.screen = screen;
23
+        translationY = 0.08f * ViewUtils.getScreenHeight();
24
+    }
25
+
26
+    public void show(boolean animate, final Runnable onAnimationEnd) {
27
+        if (animate) {
28
+            createShowAnimator(onAnimationEnd).start();
29
+        } else {
30
+            screen.setVisibility(View.VISIBLE);
31
+            NavigationApplication.instance.runOnMainThread(onAnimationEnd, 200);
32
+        }
33
+    }
34
+
35
+    public void show(boolean animate) {
36
+        if (animate) {
37
+            createShowAnimator(null).start();
38
+        } else {
39
+            screen.setVisibility(View.VISIBLE);
40
+        }
41
+    }
42
+
43
+    public void hide(boolean animate, Runnable onAnimationEnd) {
44
+        if (animate) {
45
+            createHideAnimator(onAnimationEnd).start();
46
+        } else {
47
+            screen.setVisibility(View.INVISIBLE);
48
+            onAnimationEnd.run();
49
+        }
50
+    }
51
+
52
+    private Animator createShowAnimator(final @Nullable Runnable onAnimationEnd) {
53
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(screen, View.ALPHA, 0, 1);
54
+        alpha.setInterpolator(new DecelerateInterpolator());
55
+        alpha.setDuration(200);
56
+
57
+        ObjectAnimator translationY = ObjectAnimator.ofFloat(screen, View.TRANSLATION_Y, this.translationY, 0);
58
+        translationY.setInterpolator(new DecelerateInterpolator());
59
+        translationY.setDuration(280);
60
+
61
+        AnimatorSet set = new AnimatorSet();
62
+        set.playTogether(translationY, alpha);
63
+        set.addListener(new AnimatorListenerAdapter() {
64
+            @Override
65
+            public void onAnimationStart(Animator animation) {
66
+                screen.setVisibility(View.VISIBLE);
67
+            }
68
+
69
+            @Override
70
+            public void onAnimationEnd(Animator animation) {
71
+                if (onAnimationEnd != null) {
72
+                    onAnimationEnd.run();
73
+                }
74
+            }
75
+        });
76
+        return set;
77
+    }
78
+
79
+    private Animator createHideAnimator(final Runnable onAnimationEnd) {
80
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(screen, View.ALPHA, 0);
81
+        alpha.setInterpolator(new LinearInterpolator());
82
+        alpha.setStartDelay(100);
83
+        alpha.setDuration(150);
84
+
85
+        ObjectAnimator translationY = ObjectAnimator.ofFloat(screen, View.TRANSLATION_Y, this.translationY);
86
+        translationY.setInterpolator(new AccelerateInterpolator());
87
+        translationY.setDuration(250);
88
+
89
+        AnimatorSet set = new AnimatorSet();
90
+        set.playTogether(translationY, alpha);
91
+        set.addListener(new AnimatorListenerAdapter() {
92
+            @Override
93
+            public void onAnimationEnd(Animator animation) {
94
+                onAnimationEnd.run();
95
+            }
96
+        });
97
+        return set;
98
+    }
99
+}

+ 20
- 0
android/app/src/main/java/com/reactnativenavigation/screens/ScreenFactory.java View File

@@ -0,0 +1,20 @@
1
+package com.reactnativenavigation.screens;
2
+
3
+import android.support.v7.app.AppCompatActivity;
4
+
5
+import com.reactnativenavigation.params.ScreenParams;
6
+import com.reactnativenavigation.views.LeftButtonOnClickListener;
7
+
8
+public class ScreenFactory {
9
+    public static Screen create(AppCompatActivity activity,
10
+                                ScreenParams screenParams,
11
+                                LeftButtonOnClickListener leftButtonOnClickListener) {
12
+        if (screenParams.isFragmentScreen()) {
13
+            return new FragmentScreen(activity, screenParams, leftButtonOnClickListener);
14
+        } else if (screenParams.hasTopTabs()) {
15
+            return new ViewPagerScreen(activity, screenParams, leftButtonOnClickListener);
16
+        } else {
17
+            return new SingleScreen(activity, screenParams, leftButtonOnClickListener);
18
+        }
19
+    }
20
+}

+ 256
- 0
android/app/src/main/java/com/reactnativenavigation/screens/ScreenStack.java View File

@@ -0,0 +1,256 @@
1
+package com.reactnativenavigation.screens;
2
+
3
+import android.support.annotation.Nullable;
4
+import android.support.v7.app.AppCompatActivity;
5
+import android.util.Log;
6
+import android.view.View;
7
+import android.widget.RelativeLayout;
8
+
9
+import com.reactnativenavigation.NavigationApplication;
10
+import com.reactnativenavigation.params.ScreenParams;
11
+import com.reactnativenavigation.params.StyleParams;
12
+import com.reactnativenavigation.params.TitleBarButtonParams;
13
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
14
+import com.reactnativenavigation.utils.KeyboardVisibilityDetector;
15
+import com.reactnativenavigation.utils.Task;
16
+import com.reactnativenavigation.views.LeftButtonOnClickListener;
17
+
18
+import java.util.List;
19
+import java.util.Stack;
20
+
21
+public class ScreenStack {
22
+    private static final String TAG = "ScreenStack";
23
+
24
+    public interface OnScreenPop {
25
+        void onScreenPopAnimationEnd();
26
+    }
27
+
28
+    private final AppCompatActivity activity;
29
+    private RelativeLayout parent;
30
+    private LeftButtonOnClickListener leftButtonOnClickListener;
31
+    private Stack<Screen> stack = new Stack<>();
32
+    private final KeyboardVisibilityDetector keyboardVisibilityDetector;
33
+    private boolean isStackVisible = false;
34
+    private final String navigatorId;
35
+
36
+    public String getNavigatorId() {
37
+        return navigatorId;
38
+    }
39
+
40
+    public ScreenStack(AppCompatActivity activity,
41
+                       RelativeLayout parent,
42
+                       String navigatorId,
43
+                       LeftButtonOnClickListener leftButtonOnClickListener) {
44
+        this.activity = activity;
45
+        this.parent = parent;
46
+        this.navigatorId = navigatorId;
47
+        this.leftButtonOnClickListener = leftButtonOnClickListener;
48
+        keyboardVisibilityDetector = new KeyboardVisibilityDetector(parent);
49
+    }
50
+
51
+    public void pushInitialScreen(ScreenParams initialScreenParams, RelativeLayout.LayoutParams params) {
52
+        Screen initialScreen = ScreenFactory.create(activity, initialScreenParams, leftButtonOnClickListener);
53
+        initialScreen.setVisibility(View.INVISIBLE);
54
+        addScreen(initialScreen, params);
55
+    }
56
+
57
+    public void push(final ScreenParams params, RelativeLayout.LayoutParams layoutParams) {
58
+        Screen nextScreen = ScreenFactory.create(activity, params, leftButtonOnClickListener);
59
+        final Screen previousScreen = stack.peek();
60
+        if (isStackVisible) {
61
+            pushScreenToVisibleStack(params, layoutParams, nextScreen, previousScreen);
62
+        } else {
63
+            pushScreenToInvisibleStack(layoutParams, nextScreen, previousScreen);
64
+        }
65
+    }
66
+
67
+    private void pushScreenToVisibleStack(final ScreenParams params, RelativeLayout.LayoutParams layoutParams,
68
+                                          final Screen nextScreen, final Screen previousScreen) {
69
+        nextScreen.setVisibility(View.INVISIBLE);
70
+        addScreen(nextScreen, layoutParams);
71
+        nextScreen.setOnDisplayListener(new Screen.OnDisplayListener() {
72
+            @Override
73
+            public void onDisplay() {
74
+                nextScreen.show(params.animateScreenTransitions, new Runnable() {
75
+                    @Override
76
+                    public void run() {
77
+                        removePreviousWithoutUnmount(previousScreen);
78
+                    }
79
+                });
80
+            }
81
+        });
82
+    }
83
+
84
+    private void pushScreenToInvisibleStack(RelativeLayout.LayoutParams layoutParams, Screen nextScreen,
85
+                                            Screen previousScreen) {
86
+        nextScreen.setVisibility(View.INVISIBLE);
87
+        addScreen(nextScreen, layoutParams);
88
+        removePreviousWithoutUnmount(previousScreen);
89
+    }
90
+
91
+    private void addScreen(Screen screen, RelativeLayout.LayoutParams layoutParams) {
92
+        parent.addView(screen, layoutParams);
93
+        stack.push(screen);
94
+    }
95
+
96
+    private void removePreviousWithoutUnmount(Screen previous) {
97
+        previous.preventUnmountOnDetachedFromWindow();
98
+        parent.removeView(previous);
99
+    }
100
+
101
+    public void pop(boolean animated) {
102
+        pop(animated, null);
103
+    }
104
+
105
+    public void pop(final boolean animated, @Nullable final OnScreenPop onScreenPop) {
106
+        if (!canPop()) {
107
+            return;
108
+        }
109
+
110
+        final Screen toRemove = stack.pop();
111
+        final Screen previous = stack.peek();
112
+
113
+        if (keyboardVisibilityDetector.isKeyboardVisible()) {
114
+            keyboardVisibilityDetector.setKeyboardCloseListener(new Runnable() {
115
+                @Override
116
+                public void run() {
117
+                    keyboardVisibilityDetector.setKeyboardCloseListener(null);
118
+                    swapScreens(animated, toRemove, previous, onScreenPop);
119
+                }
120
+            });
121
+            keyboardVisibilityDetector.closeKeyboard();
122
+        } else {
123
+            swapScreens(animated, toRemove, previous, onScreenPop);
124
+        }
125
+    }
126
+
127
+    private void swapScreens(boolean animated, final Screen toRemove, Screen previous, OnScreenPop onScreenPop) {
128
+        readdPrevious(previous);
129
+        previous.setStyle();
130
+        toRemove.hide(animated, new Runnable() {
131
+            @Override
132
+            public void run() {
133
+                toRemove.ensureUnmountOnDetachedFromWindow();
134
+                parent.removeView(toRemove);
135
+            }
136
+        });
137
+
138
+        if (onScreenPop != null) {
139
+            onScreenPop.onScreenPopAnimationEnd();
140
+        }
141
+    }
142
+
143
+    public Screen peek() {
144
+        return stack.peek();
145
+    }
146
+
147
+    private void readdPrevious(Screen previous) {
148
+        previous.setVisibility(View.VISIBLE);
149
+        parent.addView(previous, 0);
150
+        previous.preventMountAfterReattachedToWindow();
151
+    }
152
+
153
+    public void popToRoot(boolean animated) {
154
+        while (canPop()) {
155
+            pop(animated);
156
+        }
157
+    }
158
+
159
+    public void destroy() {
160
+        for (Screen screen : stack) {
161
+            screen.ensureUnmountOnDetachedFromWindow();
162
+            parent.removeView(screen);
163
+        }
164
+        stack.clear();
165
+    }
166
+
167
+    public int getStackSize() {
168
+        return stack.size();
169
+    }
170
+
171
+    public boolean canPop() {
172
+        return getStackSize() > 1 && !isPreviousScreenAttachedToWindow();
173
+    }
174
+
175
+    private boolean isPreviousScreenAttachedToWindow() {
176
+        Screen previousScreen = stack.get(stack.size() - 2);
177
+        if (previousScreen.getParent() != null) {
178
+            Log.w(TAG, "Can't pop stack. reason: previous screen is already attached");
179
+            return true;
180
+        }
181
+        return false;
182
+    }
183
+
184
+    public void setScreenTopBarVisible(String screenInstanceId, final boolean visible, final boolean animate) {
185
+        performOnScreen(screenInstanceId, new Task<Screen>() {
186
+            @Override
187
+            public void run(Screen param) {
188
+                param.setTopBarVisible(visible, animate);
189
+            }
190
+        });
191
+    }
192
+
193
+    public void setScreenTitleBarTitle(String screenInstanceId, final String title) {
194
+        performOnScreen(screenInstanceId, new Task<Screen>() {
195
+            @Override
196
+            public void run(Screen param) {
197
+                param.setTitleBarTitle(title);
198
+            }
199
+        });
200
+    }
201
+
202
+    public void setScreenTitleBarRightButtons(String screenInstanceId, final String navigatorEventId, final List<TitleBarButtonParams> titleBarButtons) {
203
+        performOnScreen(screenInstanceId, new Task<Screen>() {
204
+            @Override
205
+            public void run(Screen param) {
206
+                param.setTitleBarRightButtons(navigatorEventId, titleBarButtons);
207
+            }
208
+        });
209
+    }
210
+
211
+    public void setScreenTitleBarLeftButton(String screenInstanceId, final String navigatorEventId, final TitleBarLeftButtonParams titleBarLeftButtonParams) {
212
+        performOnScreen(screenInstanceId, new Task<Screen>() {
213
+            @Override
214
+            public void run(Screen param) {
215
+                param.setTitleBarLeftButton(navigatorEventId, leftButtonOnClickListener, titleBarLeftButtonParams);
216
+            }
217
+        });
218
+    }
219
+
220
+    public StyleParams getCurrentScreenStyleParams() {
221
+        return stack.peek().getStyleParams();
222
+    }
223
+
224
+    public boolean handleBackPressInJs() {
225
+        ScreenParams currentScreen = stack.peek().screenParams;
226
+        if (currentScreen.overrideBackPressInJs) {
227
+            NavigationApplication.instance.sendNavigatorEvent("backPress", currentScreen.getNavigatorEventId());
228
+            return true;
229
+        }
230
+        return false;
231
+    }
232
+
233
+    private void performOnScreen(String screenInstanceId, Task<Screen> task) {
234
+        if (stack.isEmpty()) {
235
+            return;
236
+        }
237
+
238
+        for (Screen screen : stack) {
239
+            if (screen.getScreenInstanceId().equals(screenInstanceId)) {
240
+                task.run(screen);
241
+                return;
242
+            }
243
+        }
244
+    }
245
+
246
+    public void show() {
247
+        isStackVisible = true;
248
+        stack.peek().setStyle();
249
+        stack.peek().setVisibility(View.VISIBLE);
250
+    }
251
+
252
+    public void hide() {
253
+        isStackVisible = false;
254
+        stack.peek().setVisibility(View.INVISIBLE);
255
+    }
256
+}

+ 49
- 0
android/app/src/main/java/com/reactnativenavigation/screens/SingleScreen.java View File

@@ -0,0 +1,49 @@
1
+package com.reactnativenavigation.screens;
2
+
3
+import android.support.v7.app.AppCompatActivity;
4
+
5
+import com.reactnativenavigation.params.ScreenParams;
6
+import com.reactnativenavigation.views.ContentView;
7
+import com.reactnativenavigation.views.LeftButtonOnClickListener;
8
+
9
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
10
+
11
+public class SingleScreen extends Screen {
12
+
13
+    private ContentView contentView;
14
+
15
+    public SingleScreen(AppCompatActivity activity, ScreenParams screenParams,
16
+                        LeftButtonOnClickListener titleBarBarBackButtonListener) {
17
+        super(activity, screenParams, titleBarBarBackButtonListener);
18
+    }
19
+
20
+    @Override
21
+    protected void createContent() {
22
+        contentView = new ContentView(getContext(), screenParams.screenId, screenParams.navigationParams);
23
+        LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
24
+        if (screenParams.styleParams.drawScreenBelowTopBar) {
25
+            params.addRule(BELOW, topBar.getId());
26
+        }
27
+        addView(contentView, params);
28
+    }
29
+
30
+    @Override
31
+    public void ensureUnmountOnDetachedFromWindow() {
32
+        contentView.ensureUnmountOnDetachedFromWindow();
33
+    }
34
+
35
+    @Override
36
+    public void preventUnmountOnDetachedFromWindow() {
37
+        contentView.preventUnmountOnDetachedFromWindow();
38
+    }
39
+
40
+    @Override
41
+    public void preventMountAfterReattachedToWindow() {
42
+        contentView.preventMountAfterReattachedToWindow();
43
+    }
44
+
45
+    @Override
46
+    public void setOnDisplayListener(OnDisplayListener onContentViewDisplayedListener) {
47
+        contentView.setOnDisplayListener(onContentViewDisplayedListener);
48
+    }
49
+}

+ 95
- 0
android/app/src/main/java/com/reactnativenavigation/screens/ViewPagerScreen.java View File

@@ -0,0 +1,95 @@
1
+package com.reactnativenavigation.screens;
2
+
3
+import android.support.design.widget.TabLayout;
4
+import android.support.v4.view.ViewPager;
5
+import android.support.v7.app.AppCompatActivity;
6
+
7
+import com.reactnativenavigation.params.ScreenParams;
8
+import com.reactnativenavigation.params.TopTabParams;
9
+import com.reactnativenavigation.views.ContentView;
10
+import com.reactnativenavigation.views.LeftButtonOnClickListener;
11
+
12
+import java.util.ArrayList;
13
+import java.util.List;
14
+
15
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
16
+
17
+public class ViewPagerScreen extends Screen {
18
+
19
+    private static final int OFFSCREEN_PAGE_LIMIT = 99;
20
+    private List<ContentView> contentViews;
21
+    private ViewPager viewPager;
22
+
23
+    public ViewPagerScreen(AppCompatActivity activity, ScreenParams screenParams, LeftButtonOnClickListener backButtonListener) {
24
+        super(activity, screenParams, backButtonListener);
25
+    }
26
+
27
+    @Override
28
+    protected void createContent() {
29
+        TabLayout tabLayout = topBar.initTabs();
30
+        createViewPager();
31
+        addPages();
32
+        setupViewPager(tabLayout);
33
+    }
34
+
35
+    private void createViewPager() {
36
+        viewPager = new ViewPager(getContext());
37
+        viewPager.setOffscreenPageLimit(OFFSCREEN_PAGE_LIMIT);
38
+        LayoutParams lp = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
39
+        if (screenParams.styleParams.drawScreenBelowTopBar) {
40
+            lp.addRule(BELOW, topBar.getId());
41
+        }
42
+        addView(viewPager, lp);
43
+    }
44
+
45
+    private void addPages() {
46
+        contentViews = new ArrayList<>();
47
+        for (TopTabParams tab : screenParams.topTabParams) {
48
+            ContentView contentView = new ContentView(getContext(), tab.screenId, tab.navigationParams);
49
+            addContent(contentView);
50
+            contentViews.add(contentView);
51
+        }
52
+    }
53
+
54
+    private void setupViewPager(TabLayout tabLayout) {
55
+        ContentViewPagerAdapter adapter = new ContentViewPagerAdapter(contentViews, screenParams.topTabParams);
56
+        viewPager.setAdapter(adapter);
57
+        tabLayout.setupWithViewPager(viewPager);
58
+    }
59
+
60
+    private void addContent(ContentView contentView) {
61
+        LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
62
+        viewPager.addView(contentView, params);
63
+    }
64
+
65
+    @Override
66
+    public void ensureUnmountOnDetachedFromWindow() {
67
+        for (ContentView contentView : contentViews) {
68
+            contentView.ensureUnmountOnDetachedFromWindow();
69
+        }
70
+    }
71
+
72
+    @Override
73
+    public void preventUnmountOnDetachedFromWindow() {
74
+        for (ContentView contentView : contentViews) {
75
+            contentView.preventUnmountOnDetachedFromWindow();
76
+        }
77
+    }
78
+
79
+    @Override
80
+    public void preventMountAfterReattachedToWindow() {
81
+        for (ContentView contentView : contentViews) {
82
+            contentView.preventMountAfterReattachedToWindow();
83
+        }
84
+    }
85
+
86
+    @Override
87
+    public void setOnDisplayListener(OnDisplayListener onContentViewDisplayedListener) {
88
+        contentViews.get(0).setOnDisplayListener(onContentViewDisplayedListener);
89
+    }
90
+
91
+    @Override
92
+    public String getScreenInstanceId() {
93
+        return screenParams.topTabParams.get(viewPager.getCurrentItem()).navigationParams.screenInstanceId;
94
+    }
95
+}

+ 0
- 121
android/app/src/main/java/com/reactnativenavigation/utils/BridgeUtils.java View File

@@ -1,121 +0,0 @@
1
-package com.reactnativenavigation.utils;
2
-
3
-import android.os.Bundle;
4
-import android.util.Log;
5
-
6
-import java.util.ArrayList;
7
-import java.util.HashMap;
8
-
9
-/**
10
- * Created by yedidyak on 26/05/2016.
11
- */
12
-public class BridgeUtils {
13
-
14
-    @SuppressWarnings("unchecked")
15
-    public static Bundle addMapToBundle(HashMap<String, ?> map, Bundle bundle) {
16
-        for (String key : map.keySet()) {
17
-            Object value = map.get(key);
18
-            if (value instanceof String) {
19
-                bundle.putString(key, (String) value);
20
-            } else if (value instanceof Integer) {
21
-                bundle.putInt(key, (Integer) value);
22
-            } else if (value instanceof Double) {
23
-                bundle.putDouble(key, ((Double) value));
24
-            } else if (value instanceof Boolean) {
25
-                bundle.putBoolean(key, (Boolean) value);
26
-            } else if (value instanceof HashMap) {
27
-                bundle.putBundle(key, addMapToBundle((HashMap<String, Object>) value, new Bundle()));
28
-            } else if (value instanceof ArrayList) {
29
-                putArray(key, (ArrayList) value, bundle);
30
-            }
31
-        }
32
-        return bundle;
33
-    }
34
-
35
-    @SuppressWarnings("unchecked")
36
-    private static void putArray(String key, ArrayList arrayList, Bundle bundle) {
37
-        if (arrayList.size() == 0) {
38
-            bundle.putBooleanArray(key, new boolean[]{});
39
-        } else {
40
-            verifyArrayListIsSingleType(arrayList);
41
-            if (arrayList.get(0) instanceof String) {
42
-                bundle.putStringArray(key, toStringArray((ArrayList<String>) arrayList));
43
-            } else if (arrayList.get(0) instanceof Integer) {
44
-                bundle.putIntArray(key, toIntArray((ArrayList<Integer>) arrayList));
45
-            } else if (arrayList.get(0) instanceof Float) {
46
-                bundle.putFloatArray(key, toFloatArray((ArrayList<Float>) arrayList));
47
-            } else if (arrayList.get(0) instanceof Double) {
48
-                bundle.putDoubleArray(key, toDoubleArray((ArrayList<Double>) arrayList));
49
-            } else if (arrayList.get(0) instanceof Boolean) {
50
-                bundle.putBooleanArray(key, toBooleanArray((ArrayList<Boolean>) arrayList));
51
-            } else if (arrayList.get(0) instanceof HashMap) {
52
-                bundle.putParcelableArray(key, toBundleArray((ArrayList<HashMap>) arrayList));
53
-            } else if (arrayList.get(0) instanceof ArrayList) {
54
-                Log.w("RNNavigation", "Arrays of arrays passed in props are converted to dictionaries with indexes as keys");
55
-                Bundle innerArray = new Bundle();
56
-                for (int i = 0; i < arrayList.size(); i++) {
57
-                    putArray(String.valueOf(i), (ArrayList) arrayList.get(i), innerArray);
58
-                }
59
-                bundle.putParcelable(key, innerArray);
60
-            }
61
-        }
62
-    }
63
-
64
-    private static void verifyArrayListIsSingleType(ArrayList arrayList) {
65
-        for (int i = 1; i < arrayList.size(); i++) {
66
-            if (!arrayList.get(i - 1).getClass().isInstance(arrayList.get(i))) {
67
-                throw new IllegalArgumentException("Cannot pass array of multiple types via props");
68
-            }
69
-        }
70
-    }
71
-
72
-    @SuppressWarnings("unchecked")
73
-    private static Bundle[] toBundleArray(ArrayList<HashMap> arrayList) {
74
-        Bundle[] ret = new Bundle[arrayList.size()];
75
-        for (int i=0; i < ret.length; i++) {
76
-            ret[i] = addMapToBundle(arrayList.get(i), new Bundle());
77
-        }
78
-        return ret;
79
-    }
80
-
81
-    private static int[] toIntArray(ArrayList<Integer> arrayList) {
82
-        int[] ret = new int[arrayList.size()];
83
-        for (int i=0; i < ret.length; i++) {
84
-            ret[i] = arrayList.get(i);
85
-        }
86
-        return ret;
87
-    }
88
-
89
-    private static float[] toFloatArray(ArrayList<Float> arrayList) {
90
-        float[] ret = new float[arrayList.size()];
91
-        for (int i=0; i < ret.length; i++) {
92
-            ret[i] = arrayList.get(i);
93
-        }
94
-        return ret;
95
-    }
96
-
97
-    private static double[] toDoubleArray(ArrayList<Double> arrayList) {
98
-        double[] ret = new double[arrayList.size()];
99
-        for (int i=0; i < ret.length; i++) {
100
-            ret[i] = arrayList.get(i);
101
-        }
102
-        return ret;
103
-    }
104
-
105
-    private static boolean[] toBooleanArray(ArrayList<Boolean> arrayList) {
106
-        boolean[] ret = new boolean[arrayList.size()];
107
-        for (int i=0; i < ret.length; i++) {
108
-            ret[i] = arrayList.get(i);
109
-        }
110
-        return ret;
111
-    }
112
-
113
-    private static String[] toStringArray(ArrayList<String> arrayList) {
114
-        String[] ret = new String[arrayList.size()];
115
-        for (int i=0; i < ret.length; i++) {
116
-            ret[i] = arrayList.get(i);
117
-        }
118
-        return ret;
119
-    }
120
-
121
-}

+ 0
- 14
android/app/src/main/java/com/reactnativenavigation/utils/CollectionUtils.java View File

@@ -1,14 +0,0 @@
1
-package com.reactnativenavigation.utils;
2
-
3
-import java.util.Collection;
4
-
5
-/**
6
- * Created by guyc on 11/04/16.
7
- */
8
-public class CollectionUtils {
9
-
10
-    public static boolean isNullOrEmpty(Collection collection) {
11
-        return collection == null || collection.size() == 0;
12
-    }
13
-
14
-}

+ 0
- 34
android/app/src/main/java/com/reactnativenavigation/utils/ContextProvider.java View File

@@ -1,34 +0,0 @@
1
-package com.reactnativenavigation.utils;
2
-
3
-import android.support.annotation.Nullable;
4
-
5
-import com.reactnativenavigation.activities.BaseReactActivity;
6
-
7
-import java.lang.ref.WeakReference;
8
-
9
-/**
10
- * Created by guyc on 10/03/16.
11
- */
12
-public class ContextProvider {
13
-    private static WeakReference<BaseReactActivity> sActivityWR;
14
-
15
-    public static void setActivityContext(BaseReactActivity activity) {
16
-        if (sActivityWR == null) {
17
-            sActivityWR = new WeakReference<>(activity);
18
-        }
19
-    }
20
-
21
-    /**
22
-     * Returns the currently resumed activity or {@code null} if there is none.
23
-     */
24
-    public static @Nullable BaseReactActivity getActivityContext() {
25
-        return sActivityWR != null ? sActivityWR.get() : null;
26
-    }
27
-
28
-    public static void clearActivityContext() {
29
-        if (sActivityWR != null) {
30
-            sActivityWR.clear();
31
-        }
32
-        sActivityWR = null;
33
-    }
34
-}

+ 0
- 76
android/app/src/main/java/com/reactnativenavigation/utils/IconUtils.java View File

@@ -1,76 +0,0 @@
1
-package com.reactnativenavigation.utils;
2
-
3
-import android.content.Context;
4
-import android.graphics.Bitmap;
5
-import android.graphics.BitmapFactory;
6
-import android.graphics.drawable.BitmapDrawable;
7
-import android.graphics.drawable.Drawable;
8
-import android.net.Uri;
9
-
10
-import com.reactnativenavigation.BuildConfig;
11
-
12
-import java.net.URL;
13
-
14
-/**
15
- * Created by yedidyak on 29/05/2016.
16
- */
17
-public class IconUtils {
18
-    public static final String LOCAL_RESOURCE_URI_SCHEME = "res";
19
-    private static ResourceDrawableIdHelper sResDrawableIdHelper = new ResourceDrawableIdHelper();
20
-
21
-    public static Drawable getIcon(Context ctx, String iconSource) {
22
-        return getIcon(ctx, iconSource, -1);
23
-    }
24
-
25
-    /**
26
-     * @param iconSource Icon source. In release builds this would be a path in assets, In debug it's
27
-     *                   a url and the image needs to be decoded from input stream.
28
-     * @param dimensions The requested icon dimensions
29
-     */
30
-    public static Drawable getIcon(Context ctx, String iconSource, int dimensions) {
31
-        if (iconSource == null) {
32
-            return null;
33
-        }
34
-
35
-        try {
36
-            Drawable icon;
37
-            Uri iconUri = getIconUri(ctx, iconSource);
38
-
39
-            if (LOCAL_RESOURCE_URI_SCHEME.equals(iconUri.getScheme())) {
40
-                icon = sResDrawableIdHelper.getResourceDrawable(ctx, iconSource);
41
-            } else {
42
-                URL url = new URL(iconUri.toString());
43
-                Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());
44
-                bitmap = dimensions > 0 ?
45
-                        Bitmap.createScaledBitmap(bitmap, dimensions, dimensions, false) : bitmap;
46
-                icon = new BitmapDrawable(ctx.getResources(), bitmap);
47
-            }
48
-            return icon;
49
-        } catch (Exception e) {
50
-            if (BuildConfig.DEBUG) {
51
-                e.printStackTrace();
52
-            }
53
-        }
54
-        return null;
55
-    }
56
-
57
-    private static Uri getIconUri(Context context, String iconSource) {
58
-        Uri ret = null;
59
-        if (iconSource != null) {
60
-            try {
61
-                ret = Uri.parse(iconSource);
62
-                // Verify scheme is set, so that relative uri (used by static resources) are not handled.
63
-                if (ret.getScheme() == null) {
64
-                    ret = null;
65
-                }
66
-            } catch (Exception e) {
67
-                // Ignore malformed uri, then attempt to extract resource ID.
68
-            }
69
-            if (ret == null) {
70
-                ret = sResDrawableIdHelper.getResourceDrawableUri(context, iconSource);
71
-            }
72
-        }
73
-        return ret;
74
-    }
75
-
76
-}

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

@@ -1,28 +0,0 @@
1
-package com.reactnativenavigation.utils;
2
-
3
-import android.content.Context;
4
-import android.graphics.PorterDuff;
5
-import android.graphics.PorterDuffColorFilter;
6
-import android.graphics.drawable.Drawable;
7
-
8
-/**
9
- * Created by guyc on 23/04/16.
10
- */
11
-public class ImageUtils {
12
-
13
-    public static void tint(Drawable drawable, int tint) {
14
-        drawable.setColorFilter(new PorterDuffColorFilter(tint, PorterDuff.Mode.SRC_IN));
15
-    }
16
-
17
-    /**
18
-     * This method converts dp unit to equivalent pixels, depending on device density.
19
-     *
20
-     * @param dp      A value in dp (density independent pixels) unit. Which we need to convert into pixels
21
-     * @param context Context to get resources and device specific display metrics
22
-     * @return A float value to represent px equivalent to dp depending on device density
23
-     */
24
-    public static float convertDpToPixel(float dp, Context context) {
25
-        float scale = context.getResources().getDisplayMetrics().density;
26
-        return dp * scale + 0.5f;
27
-    }
28
-}

+ 19
- 0
android/app/src/main/java/com/reactnativenavigation/utils/IntentUtils.java View File

@@ -0,0 +1,19 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import android.content.Intent;
4
+
5
+import com.reactnativenavigation.NavigationApplication;
6
+
7
+public class IntentUtils {
8
+    public static Intent getLauncherIntent() {
9
+        Intent intent = NavigationApplication.instance.getPackageManager().getLaunchIntentForPackage(NavigationApplication.instance.getPackageName());
10
+        if (intent == null)
11
+            intent = new Intent();
12
+        intent.setPackage(null);
13
+        intent.setFlags(0);
14
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
15
+        intent.setAction(Intent.ACTION_MAIN);
16
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
17
+        return intent;
18
+    }
19
+}

+ 79
- 0
android/app/src/main/java/com/reactnativenavigation/utils/KeyboardVisibilityDetector.java View File

@@ -0,0 +1,79 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import android.content.Context;
4
+import android.graphics.Rect;
5
+import android.view.View;
6
+import android.view.ViewTreeObserver;
7
+import android.view.inputmethod.InputMethodManager;
8
+
9
+import com.reactnativenavigation.NavigationApplication;
10
+
11
+public class KeyboardVisibilityDetector {
12
+    // 0.15 ratio is perhaps enough to determine keypad height.
13
+    public static final double KEYBOARD_VISIBLE_RATIO = 0.15;
14
+
15
+    private final KeyboardVisibilityLayoutListener keyboardVisibilityListener;
16
+    private final View screen;
17
+    private Runnable keyboardCloseListener;
18
+
19
+    public KeyboardVisibilityDetector(final View screen) {
20
+        this.screen = screen;
21
+        keyboardVisibilityListener = new KeyboardVisibilityLayoutListener(this);
22
+        screen.getViewTreeObserver().addOnGlobalLayoutListener(keyboardVisibilityListener);
23
+    }
24
+
25
+    public boolean isKeyboardVisible() {
26
+        return keyboardVisibilityListener.isKeyboardVisible();
27
+    }
28
+
29
+    public void setKeyboardCloseListener(Runnable keyboardCloseListener) {
30
+        this.keyboardCloseListener = keyboardCloseListener;
31
+    }
32
+
33
+    public void closeKeyboard() {
34
+        InputMethodManager imm = (InputMethodManager) screen.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
35
+        imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
36
+    }
37
+
38
+    private static class KeyboardVisibilityLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
39
+        public static final int KEYBOARD_CLOSE_DURATION = 100;
40
+        private View screen;
41
+        private boolean isVisible = false;
42
+        private KeyboardVisibilityDetector detector;
43
+
44
+        public KeyboardVisibilityLayoutListener(KeyboardVisibilityDetector detector) {
45
+            this.detector = detector;
46
+            this.screen = detector.screen;
47
+        }
48
+
49
+        public boolean isKeyboardVisible() {
50
+            return isVisible;
51
+        }
52
+
53
+        @Override
54
+        public void onGlobalLayout() {
55
+            int screenHeight = screen.getRootView().getHeight();
56
+            int screenBottomY = getScreenBottomY(screen);
57
+
58
+            int keyboardHeight = screenHeight - screenBottomY;
59
+            if (isKeyboardVisible(screenHeight, keyboardHeight)) {
60
+                isVisible = true;
61
+            } else {
62
+                if (isVisible && detector.keyboardCloseListener != null) {
63
+                    NavigationApplication.instance.runOnMainThread(detector.keyboardCloseListener, KEYBOARD_CLOSE_DURATION);
64
+                }
65
+                isVisible = false;
66
+            }
67
+        }
68
+
69
+        private boolean isKeyboardVisible(int screenHeight, int keypadHeight) {
70
+            return keypadHeight > screenHeight * KEYBOARD_VISIBLE_RATIO;
71
+        }
72
+
73
+        private int getScreenBottomY(View screen) {
74
+            Rect r = new Rect();
75
+            screen.getWindowVisibleDisplayFrame(r);
76
+            return r.bottom;
77
+        }
78
+    }
79
+}

+ 0
- 20
android/app/src/main/java/com/reactnativenavigation/utils/RefUtils.java View File

@@ -1,20 +0,0 @@
1
-package com.reactnativenavigation.utils;
2
-
3
-import java.lang.ref.WeakReference;
4
-
5
-/**
6
- * Created by guyc on 06/05/16.
7
- */
8
-public class RefUtils {
9
-    /**
10
-     * Extract the object within a WeakReference object
11
-     * @param wr the WeakReference to extract
12
-     * @return the object within the WR or null when object not available.
13
-     */
14
-    public static <T> T get(WeakReference<T> wr) {
15
-        if (wr != null) {
16
-            return wr.get();
17
-        }
18
-        return null;
19
-    }
20
-}

+ 10
- 27
android/app/src/main/java/com/reactnativenavigation/utils/ReflectionUtils.java View File

@@ -1,41 +1,25 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3
+import android.support.annotation.Nullable;
4
+
3 5
 import java.lang.reflect.Field;
4
-import java.lang.reflect.Method;
5 6
 
6
-/**
7
- * Created by guyc on 14/04/16.
8
- */
9 7
 public class ReflectionUtils {
10 8
 
11
-    public static boolean setField(Object obj, String name, Object value) {
9
+    public static void setField(Object obj, String name, Object value) {
12 10
         try {
13 11
             Field field = getField(obj.getClass(), name);
14 12
             if (field == null) {
15
-                return false;
13
+                return;
16 14
             }
17 15
             field.setAccessible(true);
18 16
             field.set(obj, value);
19
-            return true;
20 17
         } catch (Exception e) {
21 18
             e.printStackTrace();
22 19
         }
23
-        return false;
24
-    }
25
-
26
-    private static Field getField(Class clazz, String name) {
27
-        try {
28
-            return clazz.getDeclaredField(name);
29
-        } catch (NoSuchFieldException nsfe) {
30
-            return getField(clazz.getSuperclass(), name);
31
-        } catch (Exception e) {
32
-            return null;
33
-        }
34 20
     }
35 21
 
36
-    /**
37
-     * Returns the value of the field
38
-     */
22
+    @Nullable
39 23
     public static Object getDeclaredField(Object obj, String fieldName) {
40 24
         try {
41 25
             Field f = getField(obj.getClass(), fieldName);
@@ -50,14 +34,13 @@ public class ReflectionUtils {
50 34
         return null;
51 35
     }
52 36
 
53
-    public static Object invoke(Object object, String methodName) {
37
+    private static Field getField(Class clazz, String name) {
54 38
         try {
55
-            Method method = object.getClass().getDeclaredMethod(methodName);
56
-            method.setAccessible(true);
57
-            return method.invoke(object);
39
+            return clazz.getDeclaredField(name);
40
+        } catch (NoSuchFieldException nsfe) {
41
+            return getField(clazz.getSuperclass(), name);
58 42
         } catch (Exception e) {
59
-            e.printStackTrace();
43
+            return null;
60 44
         }
61
-        return null;
62 45
     }
63 46
 }

+ 0
- 13
android/app/src/main/java/com/reactnativenavigation/utils/SdkSupports.java View File

@@ -1,13 +0,0 @@
1
-package com.reactnativenavigation.utils;
2
-
3
-import android.os.Build;
4
-
5
-/**
6
- * Created by guyc on 23/04/16.
7
- */
8
-public class SdkSupports {
9
-
10
-    public static boolean lollipop() {
11
-        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
12
-    }
13
-}

+ 0
- 50
android/app/src/main/java/com/reactnativenavigation/utils/StyleHelper.java View File

@@ -1,50 +0,0 @@
1
-package com.reactnativenavigation.utils;
2
-
3
-import android.content.Context;
4
-import android.support.v4.content.ContextCompat;
5
-import android.util.Log;
6
-import android.view.Window;
7
-
8
-import com.reactnativenavigation.activities.BaseReactActivity;
9
-import com.reactnativenavigation.core.objects.Screen;
10
-import com.reactnativenavigation.views.RnnToolBar;
11
-
12
-/**
13
- * Created by guyc on 07/05/16.
14
- */
15
-public class StyleHelper {
16
-
17
-    public static void updateStyles(RnnToolBar toolBar, Screen screen) {
18
-        try {
19
-            toolBar.updateAndSetButtons(screen);
20
-            setWindowStyle(screen);
21
-        } catch (Exception e) {
22
-            Log.w("RNNavigation", "Tried to update styles with no screen!");
23
-        }
24
-    }
25
-
26
-    private static void setWindowStyle(Screen screen) {
27
-        BaseReactActivity context = ContextProvider.getActivityContext();
28
-        if (context != null) {
29
-            StyleHelper.setWindowStyle(context.getWindow(), context, screen);
30
-        }
31
-    }
32
-
33
-    public static void setWindowStyle(Window window, Context context, Screen screen) {
34
-        if (SdkSupports.lollipop()) {
35
-            final int black = ContextCompat.getColor(context, android.R.color.black);
36
-            if (screen.statusBarColor != null) {
37
-                window.setStatusBarColor(screen.statusBarColor);
38
-            } else {
39
-                window.setStatusBarColor(black);
40
-            }
41
-
42
-            if (screen.navigationBarColor != null) {
43
-                window.setNavigationBarColor(screen.navigationBarColor);
44
-            } else {
45
-                window.setNavigationBarColor(black);
46
-            }
47
-        }
48
-
49
-    }
50
-}

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/utils/Task.java View File

@@ -0,0 +1,5 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+public interface Task<T> {
4
+    void run(T param);
5
+}

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

@@ -1,9 +1,23 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3
+import android.content.Context;
4
+import android.graphics.PorterDuff;
5
+import android.graphics.PorterDuffColorFilter;
6
+import android.graphics.drawable.Drawable;
7
+import android.os.Build;
8
+import android.util.DisplayMetrics;
3 9
 import android.view.View;
4 10
 import android.view.ViewTreeObserver;
11
+import android.view.WindowManager;
12
+
13
+import com.reactnativenavigation.NavigationApplication;
14
+import com.reactnativenavigation.params.AppStyle;
15
+
16
+import java.util.concurrent.atomic.AtomicInteger;
5 17
 
6 18
 public class ViewUtils {
19
+    private static final AtomicInteger viewId = new AtomicInteger(1);
20
+
7 21
     public static void runOnPreDraw(final View view, final Runnable task) {
8 22
         view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
9 23
             @Override
@@ -17,5 +31,44 @@ public class ViewUtils {
17 31
             }
18 32
         });
19 33
     }
34
+
35
+    public static void tintDrawable(Drawable drawable, int tint, boolean enabled) {
36
+        drawable.setColorFilter(new PorterDuffColorFilter(enabled ? tint :
37
+                AppStyle.appStyle.titleBarDisabledButtonColor.getColor(),
38
+                PorterDuff.Mode.SRC_IN));
39
+    }
40
+
41
+    public static float convertDpToPixel(float dp) {
42
+        float scale = NavigationApplication.instance.getResources().getDisplayMetrics().density;
43
+        return dp * scale + 0.5f;
44
+    }
45
+
46
+    public static int generateViewId() {
47
+        if (Build.VERSION.SDK_INT >= 17) {
48
+            return View.generateViewId();
49
+        } else {
50
+            return compatGenerateViewId();
51
+        }
52
+    }
53
+
54
+    public static float getScreenHeight() {
55
+        WindowManager wm = (WindowManager) NavigationApplication.instance.getSystemService(Context.WINDOW_SERVICE);
56
+        DisplayMetrics metrics = new DisplayMetrics();
57
+        wm.getDefaultDisplay().getMetrics(metrics);
58
+        return metrics.heightPixels;
59
+    }
60
+
61
+    private static int compatGenerateViewId() {
62
+        for (; ; ) {
63
+            final int result = viewId.get();
64
+            // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
65
+            int newValue = result + 1;
66
+            if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
67
+            if (viewId.compareAndSet(result, newValue)) {
68
+                return result;
69
+            }
70
+        }
71
+    }
72
+
20 73
 }
21 74
 

+ 0
- 132
android/app/src/main/java/com/reactnativenavigation/views/BottomNavigation.java View File

@@ -1,132 +0,0 @@
1
-package com.reactnativenavigation.views;
2
-
3
-import android.animation.Animator;
4
-import android.animation.AnimatorListenerAdapter;
5
-import android.animation.ObjectAnimator;
6
-import android.content.Context;
7
-import android.support.v4.view.animation.LinearOutSlowInInterpolator;
8
-import android.util.AttributeSet;
9
-import android.util.Log;
10
-import android.view.View;
11
-
12
-import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
13
-
14
-/**
15
- * Created by guyc on 10/07/16.
16
- */
17
-public class BottomNavigation extends AHBottomNavigation {
18
-    private static final String TAG = "BottomNavigation";
19
-    public static final int SCROLL_DIRECTION_UP = 0;
20
-    public static final int SCROLL_DIRECTION_DOWN = 1;
21
-
22
-    private static final int STATE_HIDDEN = 0;
23
-    private static final int STATE_ANIMATE_HIDE = 1;
24
-    private static final int STATE_SHOWN = 2;
25
-    private static final int STATE_ANIMATE_SHOW = 3;
26
-    public static final int DURATION = 300;
27
-
28
-    private int mState = STATE_SHOWN;
29
-    private ObjectAnimator mHideAnimator;
30
-    private ObjectAnimator mShowAnimator;
31
-
32
-
33
-    public BottomNavigation(Context context) {
34
-        super(context);
35
-    }
36
-
37
-    public BottomNavigation(Context context, AttributeSet attrs) {
38
-        super(context, attrs);
39
-    }
40
-
41
-    public BottomNavigation(Context context, AttributeSet attrs, int defStyleAttr) {
42
-        super(context, attrs, defStyleAttr);
43
-    }
44
-
45
-    public void toggleTabs(boolean hide, boolean animated) {
46
-        if (hide) {
47
-            hide(animated);
48
-        } else {
49
-            show(animated);
50
-        }
51
-    }
52
-
53
-    private void hide(boolean animated) {
54
-        if (animated) {
55
-            hideAnimated();
56
-        } else {
57
-            setVisibility(View.GONE);
58
-        }
59
-    }
60
-
61
-    private void hideAnimated() {
62
-        if (mHideAnimator == null) {
63
-            mHideAnimator = createHideAnimator();
64
-        }
65
-
66
-        mHideAnimator.start();
67
-    }
68
-
69
-    private ObjectAnimator createHideAnimator() {
70
-        ObjectAnimator hideAnimator = ObjectAnimator.ofFloat(this, "translationY", getHeight());
71
-        hideAnimator.setDuration(DURATION);
72
-        hideAnimator.addListener(new AnimatorListenerAdapter() {
73
-            @Override
74
-            public void onAnimationStart(Animator animation) {
75
-                mState = STATE_ANIMATE_HIDE;
76
-            }
77
-
78
-            @Override
79
-            public void onAnimationEnd(Animator animation) {
80
-                mState = STATE_HIDDEN;
81
-            }
82
-        });
83
-        hideAnimator.setInterpolator(new LinearOutSlowInInterpolator());
84
-        return hideAnimator;
85
-
86
-    }
87
-
88
-    private void show(boolean animated) {
89
-        if (animated) {
90
-            showAnimated();
91
-        } else {
92
-            setVisibility(View.VISIBLE);
93
-        }
94
-    }
95
-
96
-    private void showAnimated() {
97
-        if (mShowAnimator == null) {
98
-            mShowAnimator = createShowAnimator();
99
-        }
100
-
101
-        mShowAnimator.start();
102
-    }
103
-
104
-    private ObjectAnimator createShowAnimator() {
105
-        ObjectAnimator showAnimator = ObjectAnimator.ofFloat(this, "translationY", 0);
106
-        showAnimator.setDuration(DURATION);
107
-        showAnimator.addListener(new AnimatorListenerAdapter() {
108
-            @Override
109
-            public void onAnimationStart(Animator animation) {
110
-                mState = STATE_ANIMATE_SHOW;
111
-            }
112
-
113
-            @Override
114
-            public void onAnimationEnd(Animator animation) {
115
-                mState = STATE_SHOWN;
116
-            }
117
-        });
118
-        showAnimator.setInterpolator(new LinearOutSlowInInterpolator());
119
-        return showAnimator;
120
-    }
121
-
122
-
123
-    public void onScroll(int direction) {
124
-        Log.d(TAG, "onScroll() called with: " + "direction = [" + direction + "]");
125
-        if (direction == SCROLL_DIRECTION_DOWN && mState == STATE_SHOWN) {
126
-            hide(true);
127
-
128
-        } else if (direction == SCROLL_DIRECTION_UP && mState == STATE_HIDDEN) {
129
-            show(true);
130
-        }
131
-    }
132
-}

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

@@ -0,0 +1,81 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+import android.graphics.Color;
5
+
6
+import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
7
+import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
8
+import com.reactnativenavigation.animation.VisibilityAnimator;
9
+import com.reactnativenavigation.params.ScreenParams;
10
+import com.reactnativenavigation.params.StyleParams;
11
+import com.reactnativenavigation.utils.ViewUtils;
12
+
13
+import java.util.List;
14
+
15
+public class BottomTabs extends AHBottomNavigation {
16
+
17
+    private VisibilityAnimator visibilityAnimator;
18
+
19
+    public BottomTabs(Context context) {
20
+        super(context);
21
+        setForceTint(true);
22
+        setId(ViewUtils.generateViewId());
23
+        createVisibilityAnimator();
24
+    }
25
+
26
+    public void addTabs(List<ScreenParams> params, OnTabSelectedListener onTabSelectedListener) {
27
+        for (ScreenParams screenParams : params) {
28
+            AHBottomNavigationItem item = new AHBottomNavigationItem(screenParams.title, screenParams.tabIcon,
29
+                    Color.GRAY);
30
+            addItem(item);
31
+            setOnTabSelectedListener(onTabSelectedListener);
32
+        }
33
+    }
34
+
35
+    public void setStyleFromScreen(StyleParams params) {
36
+        setBackgroundColor(params.bottomTabsColor);
37
+
38
+        if (params.bottomTabsButtonColor.hasColor()) {
39
+            setInactiveColor(params.bottomTabsButtonColor.getColor());
40
+        }
41
+
42
+        if (params.selectedBottomTabsButtonColor.hasColor()) {
43
+            setAccentColor(params.selectedBottomTabsButtonColor.getColor());
44
+        }
45
+
46
+        setForceTitlesDisplay(params.forceTitlesDisplay);
47
+
48
+        setVisibility(params.bottomTabsHidden, true);
49
+    }
50
+
51
+    public void setVisibility(boolean hidden, boolean animated) {
52
+        if (visibilityAnimator != null) {
53
+            visibilityAnimator.setVisible(!hidden, animated);
54
+        } else {
55
+            setVisibility(hidden);
56
+        }
57
+    }
58
+
59
+    private void setBackgroundColor(StyleParams.Color bottomTabsColor) {
60
+        if (bottomTabsColor.hasColor()) {
61
+            setDefaultBackgroundColor(bottomTabsColor.getColor());
62
+        } else {
63
+            setDefaultBackgroundColor(Color.WHITE);
64
+        }
65
+    }
66
+
67
+    private void setVisibility(boolean bottomTabsHidden) {
68
+        setVisibility(bottomTabsHidden ? GONE : VISIBLE);
69
+    }
70
+
71
+    private void createVisibilityAnimator() {
72
+        ViewUtils.runOnPreDraw(this, new Runnable() {
73
+            @Override
74
+            public void run() {
75
+                visibilityAnimator = new VisibilityAnimator(BottomTabs.this,
76
+                        VisibilityAnimator.HideDirection.Down,
77
+                        getHeight());
78
+            }
79
+        });
80
+    }
81
+}

+ 73
- 0
android/app/src/main/java/com/reactnativenavigation/views/ContentView.java View File

@@ -0,0 +1,73 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+import android.view.View;
5
+
6
+import com.facebook.react.ReactRootView;
7
+import com.reactnativenavigation.NavigationApplication;
8
+import com.reactnativenavigation.params.NavigationParams;
9
+import com.reactnativenavigation.react.ReactViewHacks;
10
+import com.reactnativenavigation.screens.SingleScreen;
11
+import com.reactnativenavigation.utils.ViewUtils;
12
+
13
+public class ContentView extends ReactRootView {
14
+
15
+    private final String screenId;
16
+    private final NavigationParams navigationParams;
17
+
18
+    boolean isContentVisible = false;
19
+    private SingleScreen.OnDisplayListener onDisplayListener;
20
+
21
+    public ContentView(Context context, String screenId, NavigationParams navigationParams) {
22
+        super(context);
23
+        this.screenId = screenId;
24
+        this.navigationParams = navigationParams;
25
+        attachToJS();
26
+    }
27
+
28
+    public String getNavigatorEventId() {
29
+        return navigationParams.navigatorEventId;
30
+    }
31
+
32
+    public void preventUnmountOnDetachedFromWindow() {
33
+        ReactViewHacks.preventUnmountOnDetachedFromWindow(this);
34
+    }
35
+
36
+    public void ensureUnmountOnDetachedFromWindow() {
37
+        ReactViewHacks.ensureUnmountOnDetachedFromWindow(this);
38
+    }
39
+
40
+    public void preventMountAfterReattachedToWindow() {
41
+        ReactViewHacks.preventMountAfterReattachedToWindow(this);
42
+    }
43
+
44
+    @Override
45
+    public void onViewAdded(final View child) {
46
+        super.onViewAdded(child);
47
+        detectContentViewVisible(child);
48
+    }
49
+
50
+    private void detectContentViewVisible(View child) {
51
+        if (onDisplayListener != null) {
52
+            ViewUtils.runOnPreDraw(child, new Runnable() {
53
+                @Override
54
+                public void run() {
55
+                    if (!isContentVisible) {
56
+                        isContentVisible = true;
57
+                        onDisplayListener.onDisplay();
58
+                        onDisplayListener = null;
59
+                    }
60
+                }
61
+            });
62
+        }
63
+    }
64
+
65
+    private void attachToJS() {
66
+        startReactApplication(NavigationApplication.instance.getReactGateway().getReactInstanceManager(), screenId,
67
+                navigationParams.toBundle());
68
+    }
69
+
70
+    public void setOnDisplayListener(SingleScreen.OnDisplayListener onDisplayListener) {
71
+        this.onDisplayListener = onDisplayListener;
72
+    }
73
+}

+ 85
- 0
android/app/src/main/java/com/reactnativenavigation/views/LeftButton.java View File

@@ -0,0 +1,85 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+import android.graphics.Color;
5
+import android.view.View;
6
+
7
+import com.balysv.materialmenu.MaterialMenuDrawable;
8
+import com.reactnativenavigation.NavigationApplication;
9
+import com.reactnativenavigation.params.TitleBarButtonParams;
10
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
11
+
12
+// TODO: replace with vector menu drawable
13
+public class LeftButton extends MaterialMenuDrawable implements View.OnClickListener {
14
+
15
+    private static int getColor(TitleBarButtonParams params) {
16
+        return params != null && params.color.hasColor() ?
17
+                params.color.getColor() :
18
+                Color.BLACK;
19
+    }
20
+
21
+    private TitleBarLeftButtonParams params;
22
+    private final LeftButtonOnClickListener onClickListener;
23
+    private final String navigatorEventId;
24
+    private final boolean overrideBackPressInJs;
25
+
26
+    public LeftButton(Context context,
27
+                      TitleBarLeftButtonParams params,
28
+                      LeftButtonOnClickListener onClickListener,
29
+                      String navigatorEventId,
30
+                      boolean overrideBackPressInJs) {
31
+        super(context, getColor(params), Stroke.THIN);
32
+        this.params = params;
33
+        this.onClickListener = onClickListener;
34
+        this.navigatorEventId = navigatorEventId;
35
+        this.overrideBackPressInJs = overrideBackPressInJs;
36
+        setInitialState();
37
+    }
38
+
39
+    public void setIconState(TitleBarLeftButtonParams params) {
40
+        this.params = params;
41
+        if (params.color.hasColor()) {
42
+            setColor(params.color.getColor());
43
+        }
44
+        animateIconState(params.iconState);
45
+    }
46
+
47
+    @Override
48
+    public void onClick(View v) {
49
+        if (isBackButton()) {
50
+            handleBackButtonClick();
51
+        } else if (isSideMenuButton()) {
52
+            onClickListener.onSideMenuButtonClick();
53
+        } else {
54
+            sendClickEvent();
55
+        }
56
+    }
57
+
58
+    private void handleBackButtonClick() {
59
+        if (overrideBackPressInJs) {
60
+            NavigationApplication.instance.sendNavigatorEvent("backPress", navigatorEventId);
61
+        } else {
62
+            onClickListener.onTitleBarBackButtonClick();
63
+        }
64
+    }
65
+
66
+    private void setInitialState() {
67
+        if (params != null) {
68
+            setIconState(params.iconState);
69
+        } else {
70
+            setVisible(false);
71
+        }
72
+    }
73
+
74
+    private boolean isBackButton() {
75
+        return getIconState() == IconState.ARROW;
76
+    }
77
+
78
+    private boolean isSideMenuButton() {
79
+        return getIconState() == IconState.BURGER;
80
+    }
81
+
82
+    private void sendClickEvent() {
83
+        NavigationApplication.instance.sendNavigatorEvent(params.eventId, navigatorEventId);
84
+    }
85
+}

+ 7
- 0
android/app/src/main/java/com/reactnativenavigation/views/LeftButtonOnClickListener.java View File

@@ -0,0 +1,7 @@
1
+package com.reactnativenavigation.views;
2
+
3
+public interface LeftButtonOnClickListener {
4
+    boolean onTitleBarBackButtonClick();
5
+
6
+    void onSideMenuButtonClick();
7
+}

+ 0
- 195
android/app/src/main/java/com/reactnativenavigation/views/RctView.java View File

@@ -1,195 +0,0 @@
1
-package com.reactnativenavigation.views;
2
-
3
-import android.os.Bundle;
4
-import android.view.View;
5
-import android.view.ViewGroup;
6
-import android.view.ViewTreeObserver;
7
-import android.widget.FrameLayout;
8
-import android.widget.ScrollView;
9
-
10
-import com.facebook.react.ReactInstanceManager;
11
-import com.facebook.react.ReactRootView;
12
-import com.reactnativenavigation.activities.BaseReactActivity;
13
-import com.reactnativenavigation.activities.BottomTabActivity;
14
-import com.reactnativenavigation.core.objects.Screen;
15
-import com.reactnativenavigation.utils.BridgeUtils;
16
-import com.reactnativenavigation.utils.ReflectionUtils;
17
-
18
-/**
19
- * Created by guyc on 10/03/16.
20
- */
21
-public class RctView extends FrameLayout {
22
-
23
-    private BottomTabActivity context;
24
-    private ReactRootView reactRootView;
25
-    private ScrollView scrollView;
26
-    private int lastScrollY = -1;
27
-    private final ViewTreeObserver.OnScrollChangedListener scrollChangedListener = new ViewTreeObserver.OnScrollChangedListener() {
28
-        @Override
29
-        public void onScrollChanged() {
30
-            if (!scrollView.getViewTreeObserver().isAlive()) {
31
-                return;
32
-            }
33
-
34
-            final int scrollY = scrollView.getScrollY();
35
-            if (scrollY != lastScrollY && // Scroll position changed
36
-                scrollY > 0 && // Ignore top overscroll
37
-                scrollY < (scrollView.getChildAt(0).getHeight() - scrollView.getHeight())) { // Ignore bottom overscroll
38
-                int direction = scrollY > lastScrollY ?
39
-                        BottomNavigation.SCROLL_DIRECTION_DOWN :
40
-                        BottomNavigation.SCROLL_DIRECTION_UP;
41
-                lastScrollY = scrollY;
42
-                context.onScrollChanged(direction);
43
-            }
44
-        }
45
-    };
46
-    private boolean isScrollEventListenerRegistered = false;
47
-
48
-    private final View.OnAttachStateChangeListener stateChangeListener =
49
-            new View.OnAttachStateChangeListener() {
50
-                @Override
51
-                public void onViewAttachedToWindow(View v) {
52
-                    scrollView = getScrollView((ViewGroup) getParent());
53
-
54
-                    if (scrollView != null && !isScrollEventListenerRegistered) {
55
-                        addScrollListener();
56
-                    }
57
-                }
58
-
59
-                @Override
60
-                public void onViewDetachedFromWindow(final View detachedView) {
61
-                    removeScrollListener();
62
-
63
-                    post(new Runnable() {
64
-                        @Override
65
-                        public void run() {
66
-                            scrollView = getScrollView((ViewGroup) getParent());
67
-                            if (scrollView != null && !isScrollEventListenerRegistered) {
68
-                                isScrollEventListenerRegistered = true;
69
-                                addScrollListener();
70
-                            }
71
-                        }
72
-                    });
73
-                }
74
-            };
75
-
76
-    public void detachReactRootView(ReactInstanceManager reactInstanceManager) {
77
-        reactInstanceManager.detachRootView(reactRootView);
78
-    }
79
-
80
-    /**
81
-     * Interface used to run some code when the {@link ReactRootView} is visible.
82
-     */
83
-    public interface OnDisplayedListener {
84
-        /**
85
-         * This method will be invoked when the {@link ReactRootView} is visible.
86
-         */
87
-        void onDisplayed();
88
-    }
89
-
90
-    @SuppressWarnings("unchecked")
91
-    public RctView(BaseReactActivity ctx, ReactInstanceManager rctInstanceManager, final Screen screen,
92
-                   final OnDisplayedListener onDisplayedListener) {
93
-        super(ctx);
94
-        setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
95
-
96
-        final OnDisplayedListener onDisplayedListenerInternal = screen.bottomTabsHiddenOnScroll ?
97
-            new OnDisplayedListener() {
98
-                @Override
99
-                public void onDisplayed() {
100
-                    if (onDisplayedListener != null) {
101
-                        onDisplayedListener.onDisplayed();
102
-                    }
103
-
104
-                    setupScrollViewWithBottomTabs();
105
-                }
106
-            } : onDisplayedListener;
107
-
108
-        reactRootView = new RnnReactRootView(ctx, onDisplayedListenerInternal);
109
-        reactRootView.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
110
-        Bundle passProps = createPassProps(screen);
111
-        String componentName = screen.screenId;
112
-        reactRootView.startReactApplication(rctInstanceManager, componentName, passProps);
113
-
114
-        addView(reactRootView);
115
-    }
116
-
117
-    private Bundle createPassProps(Screen screen) {
118
-        Bundle passProps = new Bundle();
119
-        passProps.putString(Screen.KEY_SCREEN_INSTANCE_ID, screen.screenInstanceId);
120
-        passProps.putString(Screen.KEY_NAVIGATOR_ID, screen.navigatorId);
121
-        passProps.putString(Screen.KEY_NAVIGATOR_EVENT_ID, screen.navigatorEventId);
122
-        if (screen.passedProps != null) {
123
-            BridgeUtils.addMapToBundle(screen.passedProps, passProps);
124
-        }
125
-        return passProps;
126
-    }
127
-
128
-    private void setupScrollViewWithBottomTabs() {
129
-        scrollView = getScrollView((ViewGroup) getParent());
130
-        if (scrollView != null) {
131
-            context = (BottomTabActivity) getContext();
132
-            attachStateChangeListener(scrollView);
133
-            addScrollListener();
134
-        }
135
-    }
136
-
137
-    private ScrollView getScrollView(ViewGroup parent) {
138
-        for (int i = 0; i < parent.getChildCount(); i++) {
139
-            View child = parent.getChildAt(i);
140
-
141
-            if (child instanceof ScrollView) {
142
-                return (ScrollView) child;
143
-            }
144
-
145
-            if (child instanceof ViewGroup) {
146
-                return getScrollView((ViewGroup) child);
147
-            }
148
-        }
149
-
150
-        return null;
151
-    }
152
-
153
-    private void attachStateChangeListener(ScrollView scrollView) {
154
-        scrollView.addOnAttachStateChangeListener(stateChangeListener);
155
-    }
156
-
157
-    private void addScrollListener() {
158
-        scrollView.getViewTreeObserver().addOnScrollChangedListener(scrollChangedListener);
159
-    }
160
-
161
-    private void removeScrollListener() {
162
-        scrollView.getViewTreeObserver().removeOnScrollChangedListener(scrollChangedListener);
163
-    }
164
-
165
-    /**
166
-     * Must be called before view is removed from screen, but will be added again later. Setting mAttachScheduled
167
-     * to true will prevent the component from getting unmounted once view is detached from screen.
168
-     */
169
-    public void onTemporallyRemovedFromScreen() {
170
-        // Hack in order to prevent the react view from getting unmounted
171
-
172
-        ReflectionUtils.setField(reactRootView, "mAttachScheduled", true);
173
-    }
174
-
175
-    /**
176
-     * Must be called before view is removed from screen inorder to ensure onDetachedFromScreen is properly
177
-     * executed and componentWillUnmount is called
178
-     */
179
-    public void onRemoveFromScreen() {
180
-        ReflectionUtils.setField(reactRootView, "mAttachScheduled", false);
181
-    }
182
-
183
-    /**
184
-     * Must be called when view is added again to screen inorder to ensure onDetachedFromScreen is properly
185
-     * executed and componentWillUnmount is called
186
-     */
187
-    public void onReAddToScreen() {
188
-        ReflectionUtils.setField(reactRootView, "mAttachScheduled", false);
189
-    }
190
-
191
-    public void detachFromScreen() {
192
-        ReflectionUtils.invoke(reactRootView, "onDetachedFromWindow");
193
-    }
194
-}
195
-

+ 0
- 34
android/app/src/main/java/com/reactnativenavigation/views/RnnReactRootView.java View File

@@ -1,34 +0,0 @@
1
-package com.reactnativenavigation.views;
2
-
3
-import android.content.Context;
4
-import android.view.ViewTreeObserver;
5
-
6
-import com.facebook.react.ReactRootView;
7
-
8
-/**
9
- * Created by guyc on 11/07/16.
10
- */
11
-public class RnnReactRootView extends ReactRootView implements ViewTreeObserver.OnGlobalLayoutListener {
12
-    private final RctView.OnDisplayedListener mOnDisplayedListener;
13
-
14
-    public RnnReactRootView(Context context, RctView.OnDisplayedListener onDisplayedListener) {
15
-        super(context);
16
-        mOnDisplayedListener = onDisplayedListener;
17
-
18
-        if (onDisplayedListener != null) {
19
-            detectOnDisplay();
20
-        }
21
-    }
22
-
23
-    private void detectOnDisplay() {
24
-        getViewTreeObserver().addOnGlobalLayoutListener(this);
25
-    }
26
-
27
-    @Override
28
-    public void onGlobalLayout() {
29
-        if (getChildCount() >= 1) {
30
-            getViewTreeObserver().removeOnGlobalLayoutListener(this);
31
-            mOnDisplayedListener.onDisplayed();
32
-        }
33
-    }
34
-}

+ 0
- 77
android/app/src/main/java/com/reactnativenavigation/views/RnnTabLayout.java View File

@@ -1,77 +0,0 @@
1
-package com.reactnativenavigation.views;
2
-
3
-import android.content.Context;
4
-import android.content.res.ColorStateList;
5
-import android.content.res.TypedArray;
6
-import android.graphics.drawable.Drawable;
7
-import android.support.design.widget.TabLayout;
8
-import android.util.AttributeSet;
9
-import android.util.TypedValue;
10
-
11
-import com.reactnativenavigation.R;
12
-import com.reactnativenavigation.core.objects.Screen;
13
-
14
-/**
15
- * Created by guyc on 07/05/16.
16
- */
17
-public class RnnTabLayout extends TabLayout {
18
-    private Drawable mBackground;
19
-    private ColorStateList mTabTextColors;
20
-    private int mSelectedTabIndicatorColor;
21
-
22
-    public RnnTabLayout(Context context) {
23
-        this(context, null);
24
-    }
25
-
26
-    public RnnTabLayout(Context context, AttributeSet attrs) {
27
-        this(context, attrs, 0);
28
-    }
29
-
30
-    public RnnTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
31
-        super(context, attrs, defStyleAttr);
32
-        init(context);
33
-    }
34
-
35
-    private void init(Context ctx) {
36
-        mBackground = getBackground();
37
-        mTabTextColors = getTabTextColors();
38
-
39
-        // Get default accent color which is used as the selected tab indicator color
40
-        TypedValue typedValue = new TypedValue();
41
-        TypedArray a = ctx.obtainStyledAttributes(typedValue.data, new int[]{R.attr.colorAccent});
42
-        mSelectedTabIndicatorColor = a.getColor(0, 0);
43
-        a.recycle();
44
-    }
45
-
46
-    public void setStyle(Screen screen) {
47
-        if (screen.toolBarColor != null) {
48
-            setBackgroundColor(screen.toolBarColor);
49
-        } else {
50
-            resetBackground();
51
-        }
52
-
53
-        if (screen.tabNormalTextColor != null && screen.tabSelectedTextColor != null) {
54
-            setTabTextColors(screen.tabNormalTextColor, screen.tabSelectedTextColor);
55
-        } else {
56
-            resetTextColors();
57
-        }
58
-
59
-        if (screen.tabIndicatorColor != null) {
60
-            setSelectedTabIndicatorColor(screen.tabIndicatorColor);
61
-        } else {
62
-            resetSelectedTabIndicatorColor();
63
-        }
64
-    }
65
-
66
-    public void resetBackground() {
67
-        setBackground(mBackground);
68
-    }
69
-
70
-    public void resetTextColors() {
71
-        setTabTextColors(mTabTextColors);
72
-    }
73
-
74
-    public void resetSelectedTabIndicatorColor() {
75
-        setSelectedTabIndicatorColor(mSelectedTabIndicatorColor);
76
-    }
77
-}

+ 0
- 439
android/app/src/main/java/com/reactnativenavigation/views/RnnToolBar.java View File

@@ -1,439 +0,0 @@
1
-package com.reactnativenavigation.views;
2
-
3
-import android.app.Activity;
4
-import android.content.Context;
5
-import android.graphics.Color;
6
-import android.graphics.drawable.Drawable;
7
-import android.os.AsyncTask;
8
-import android.support.annotation.ColorInt;
9
-import android.support.annotation.NonNull;
10
-import android.support.annotation.UiThread;
11
-import android.support.v4.content.ContextCompat;
12
-import android.support.v4.widget.DrawerLayout;
13
-import android.support.v7.app.ActionBar;
14
-import android.support.v7.app.ActionBarDrawerToggle;
15
-import android.support.v7.app.AppCompatActivity;
16
-import android.support.v7.graphics.drawable.DrawerArrowDrawable;
17
-import android.support.v7.widget.Toolbar;
18
-import android.util.AttributeSet;
19
-import android.view.Gravity;
20
-import android.view.Menu;
21
-import android.view.MenuItem;
22
-import android.view.View;
23
-import android.view.ViewTreeObserver;
24
-import android.widget.TextView;
25
-
26
-import com.reactnativenavigation.R;
27
-import com.reactnativenavigation.activities.BaseReactActivity;
28
-import com.reactnativenavigation.core.objects.Button;
29
-import com.reactnativenavigation.core.objects.Screen;
30
-import com.reactnativenavigation.utils.ContextProvider;
31
-import com.reactnativenavigation.utils.IconUtils;
32
-import com.reactnativenavigation.utils.ImageUtils;
33
-
34
-import java.lang.ref.WeakReference;
35
-import java.util.ArrayList;
36
-import java.util.HashMap;
37
-import java.util.List;
38
-import java.util.Map;
39
-
40
-/**
41
- * Created by guyc on 09/04/16.
42
- */
43
-public class RnnToolBar extends Toolbar {
44
-
45
-    private List<Screen> mScreens;
46
-    private AsyncTask mDrawerIconTask;
47
-    private AsyncTask mSetupToolbarTask;
48
-    private Drawable mBackground;
49
-    private Drawable mDrawerIcon;
50
-    private DrawerLayout mDrawerLayout;
51
-    private ActionBarDrawerToggle mDrawerToggle;
52
-    private ArrayList<View> mMenuItems;
53
-
54
-    public RnnToolBar(Context context) {
55
-        super(context);
56
-        init();
57
-    }
58
-
59
-    public RnnToolBar(Context context, AttributeSet attrs) {
60
-        super(context, attrs);
61
-        init();
62
-    }
63
-
64
-    public RnnToolBar(Context context, AttributeSet attrs, int defStyleAttr) {
65
-        super(context, attrs, defStyleAttr);
66
-        init();
67
-    }
68
-
69
-    private void init() {
70
-        mMenuItems = new ArrayList<>();
71
-        mBackground = getBackground();
72
-    }
73
-
74
-    public void setScreens(List<Screen> screens) {
75
-        mScreens = screens;
76
-    }
77
-
78
-    public void setStyle(Screen screen) {
79
-        if (screen.toolBarColor != null) {
80
-            setBackgroundColor(screen.toolBarColor);
81
-        } else {
82
-            resetBackground();
83
-        }
84
-
85
-        if (screen.navBarTextColor != null) {
86
-            setTitleTextColor(screen.navBarTextColor);
87
-        } else {
88
-            resetTitleTextColor();
89
-        }
90
-
91
-        if (screen.toolBarHidden != null && screen.toolBarHidden) {
92
-            hideToolbar();
93
-        } else {
94
-            showToolbar();
95
-        }
96
-    }
97
-
98
-    private void resetBackground() {
99
-        setBackground(mBackground);
100
-    }
101
-
102
-    private void resetTitleTextColor() {
103
-        setTitleTextColor(ContextCompat.getColor(getContext(), android.R.color.primary_text_light));
104
-    }
105
-
106
-    public void handleOnCreateOptionsMenuAsync() {
107
-        if (mScreens != null) {
108
-            setupToolbarButtonsAsync(null, mScreens.get(0));
109
-        }
110
-    }
111
-
112
-    public ActionBarDrawerToggle setupDrawer(DrawerLayout drawerLayout, Screen drawerScreen, Screen screen) {
113
-        if (drawerLayout == null || drawerScreen == null) {
114
-            return null;
115
-        }
116
-
117
-        mDrawerLayout = drawerLayout;
118
-        mDrawerToggle = new ActionBarDrawerToggle(
119
-            ContextProvider.getActivityContext(),
120
-            mDrawerLayout,
121
-            this,
122
-            R.string.drawer_open,
123
-            R.string.drawer_close
124
-        );
125
-        mDrawerLayout.setDrawerListener(mDrawerToggle);
126
-        setupDrawerIconAsync(drawerScreen.icon, screen);
127
-
128
-        return mDrawerToggle;
129
-    }
130
-
131
-    public void setDrawerIcon(Drawable icon) {
132
-        mDrawerIcon = icon;
133
-    }
134
-
135
-    public void showDrawer(boolean animated) {
136
-        if (mDrawerLayout == null) {
137
-            return;
138
-        }
139
-
140
-        mDrawerLayout.openDrawer(Gravity.LEFT);
141
-    }
142
-
143
-    public void hideDrawer(boolean animated) {
144
-        if (mDrawerLayout == null) {
145
-            return;
146
-        }
147
-
148
-        mDrawerLayout.closeDrawer(Gravity.LEFT);
149
-    }
150
-
151
-    public void toggleDrawer(boolean animated) {
152
-        if (mDrawerLayout == null) {
153
-            return;
154
-        }
155
-
156
-        boolean visible = mDrawerLayout.isDrawerOpen(Gravity.LEFT);
157
-        if (visible) {
158
-            hideDrawer(animated);
159
-        } else {
160
-            showDrawer(animated);
161
-        }
162
-    }
163
-
164
-    public void setupDrawerIconAsync(String drawerIconSource, Screen screen) {
165
-        if (mDrawerIconTask == null) {
166
-            mDrawerIconTask = new SetupDrawerIconTask(this, drawerIconSource, screen).execute();
167
-        }
168
-    }
169
-
170
-    public void setupToolbarButtonsAsync(Screen newScreen) {
171
-        if (newScreen != null) {
172
-            this.setupToolbarButtonsAsync(null, newScreen);
173
-        }
174
-    }
175
-
176
-
177
-    public void setupToolbarButtonsAsync(Screen oldScreen, Screen newScreen) {
178
-        if (mSetupToolbarTask == null) {
179
-            mSetupToolbarTask = new SetupToolbarButtonsTask(this, oldScreen, newScreen).execute();
180
-        }
181
-    }
182
-
183
-    public void showToolbar(boolean animated) {
184
-        ActionBar actionBar = ((AppCompatActivity) getContext()).getSupportActionBar();
185
-        if (actionBar != null) {
186
-            actionBar.setShowHideAnimationEnabled(animated);
187
-            // We hide the ToolBar's parent (AppBarLayout) since this animates the shadow added by AppBar as well
188
-            ((View) getParent()).setVisibility(VISIBLE);
189
-        }
190
-    }
191
-
192
-    public void hideToolbar(boolean animated) {
193
-        ActionBar actionBar = ((AppCompatActivity) getContext()).getSupportActionBar();
194
-        if (actionBar != null) {
195
-            actionBar.setShowHideAnimationEnabled(animated);
196
-            // We hide the ToolBar's parent (AppBarLayout) since this animates the shadow added by AppBar as well
197
-            ((View) getParent()).setVisibility(GONE);
198
-        }
199
-    }
200
-
201
-    private void showToolbar() {
202
-        showToolbar(false);
203
-    }
204
-
205
-    private void hideToolbar() {
206
-        hideToolbar(false);
207
-    }
208
-
209
-    public void setNavUpButton() {
210
-        setNavUpButton(null);
211
-    }
212
-
213
-    @SuppressWarnings({"ConstantConditions"})
214
-    public void setNavUpButton(Screen screen) {
215
-        BaseReactActivity context = ContextProvider.getActivityContext();
216
-        if (context == null) {
217
-            return;
218
-        }
219
-
220
-        ActionBar actionBar = context.getSupportActionBar();
221
-        if (actionBar == null) {
222
-            return;
223
-        }
224
-
225
-        boolean isBack = screen != null;
226
-        boolean hasDrawer = mDrawerToggle != null;
227
-
228
-        Drawable navIcon = null;
229
-        DrawerArrowDrawable navArrow = null;
230
-        if (hasDrawer && mDrawerIcon == null) {
231
-            navArrow = (DrawerArrowDrawable) this.getNavigationIcon();
232
-        } else {
233
-            if (isBack && !screen.backButtonHidden) {
234
-                navArrow = new DrawerArrowDrawable(context);
235
-            } else if (hasDrawer) {
236
-                navIcon = mDrawerIcon;
237
-            }
238
-        }
239
-
240
-        if (navArrow != null) {
241
-            navArrow.setProgress(isBack ? 1.0f : 0.0f);
242
-            if (screen != null && screen.navBarButtonColor != null) {
243
-                navArrow.setColor(screen.navBarButtonColor);
244
-            } else {
245
-                navArrow.setColor(Color.BLACK);
246
-            }
247
-            navIcon = navArrow;
248
-        }
249
-
250
-        actionBar.setHomeAsUpIndicator(navIcon);
251
-        actionBar.setDisplayHomeAsUpEnabled(navIcon != null);
252
-    }
253
-
254
-    /**
255
-     * Update the ToolBar from screen. This function sets any properties that are defined
256
-     * in the screen.
257
-     * @param screen The currently displayed screen
258
-     */
259
-    @UiThread
260
-    public void update(@NonNull Screen screen) {
261
-        ((AppCompatActivity) getContext()).setSupportActionBar(this);
262
-        setTitle(screen.title);
263
-        setStyle(screen);
264
-    }
265
-
266
-    public void updateAndSetButtons(Screen screen) {
267
-        update(screen);
268
-        setupToolbarButtonsAsync(screen);
269
-    }
270
-
271
-    private static class SetupDrawerIconTask extends AsyncTask<Void, Void, Drawable> {
272
-        private final WeakReference<RnnToolBar> mToolbarWR;
273
-        private final String mDrawerIconSource;
274
-        private final Integer mTintColor;
275
-
276
-        public SetupDrawerIconTask(RnnToolBar toolBar, String drawerIconSource, Screen screen) {
277
-            mToolbarWR = new WeakReference<>(toolBar);
278
-            mDrawerIconSource = drawerIconSource;
279
-            mTintColor = screen.navBarButtonColor;
280
-        }
281
-
282
-        @Override
283
-        protected Drawable doInBackground(Void... params) {
284
-            Context context = ContextProvider.getActivityContext();
285
-            if (context == null || mDrawerIconSource == null) {
286
-                return null;
287
-            }
288
-
289
-            return IconUtils.getIcon(context, mDrawerIconSource);
290
-        }
291
-
292
-        @Override
293
-        protected void onPostExecute(Drawable drawerIcon) {
294
-            RnnToolBar toolBar = mToolbarWR.get();
295
-            if (drawerIcon != null) {
296
-                if (mTintColor != null) {
297
-                    ImageUtils.tint(drawerIcon, mTintColor);
298
-                }
299
-                toolBar.setDrawerIcon(drawerIcon);
300
-            }
301
-
302
-            toolBar.setNavUpButton();
303
-            mToolbarWR.clear();
304
-        }
305
-    }
306
-
307
-    private static class SetupToolbarButtonsTask extends AsyncTask<Void, Void, Map<String, Drawable>> {
308
-        private final List<Button> mOldButtons;
309
-        private final List<Button> mNewButtons;
310
-        private final WeakReference<RnnToolBar> mToolbarWR;
311
-        @ColorInt private final Integer mTintColor;
312
-        @ColorInt private final Integer mButtonTextColor;
313
-        private final int mIconDimensions;
314
-
315
-        public SetupToolbarButtonsTask(RnnToolBar toolBar, Screen oldScreen, Screen newScreen) {
316
-            mToolbarWR = new WeakReference<>(toolBar);
317
-            mOldButtons = oldScreen == null ? null : oldScreen.getButtons();
318
-            mNewButtons = newScreen.getButtons();
319
-            mTintColor = newScreen.navBarButtonColor;
320
-            mButtonTextColor = newScreen.navBarTextColor;
321
-            mIconDimensions = (int) ImageUtils.convertDpToPixel(48, toolBar.getContext());
322
-        }
323
-
324
-        @Override
325
-        protected Map<String, Drawable> doInBackground(Void... params) {
326
-            Context context = ContextProvider.getActivityContext();
327
-            if (context == null) {
328
-                return null;
329
-            }
330
-
331
-            Map<String, Drawable> icons = new HashMap<>();
332
-            for (Button button : mNewButtons) {
333
-                if (button.hasIcon()) {
334
-                    icons.put(button.id, button.getIcon(context, mIconDimensions));
335
-                }
336
-            }
337
-            return icons;
338
-        }
339
-
340
-        @Override
341
-        protected void onPostExecute(Map<String, Drawable> icons) {
342
-            final Context context = ContextProvider.getActivityContext();
343
-            if (context == null) {
344
-                return;
345
-            }
346
-
347
-            Menu menu = ((BaseReactActivity) context).getMenu();
348
-            if (menu == null) {
349
-                RnnToolBar toolBar = mToolbarWR.get();
350
-                if (toolBar != null) {
351
-                    toolBar.mSetupToolbarTask = null;
352
-                }
353
-                mToolbarWR.clear();
354
-                return;
355
-            }
356
-
357
-            // Remove prev screen buttons
358
-            if(mOldButtons == null) {
359
-                menu.clear();
360
-            } else {
361
-                for (Button btn : mOldButtons) {
362
-                    menu.removeItem(btn.getItemId());
363
-                }
364
-            }
365
-
366
-            // Add new screen buttons
367
-            final List<String> textButtons = new ArrayList<>();
368
-            final int size = mNewButtons.size();
369
-            for (int i = 0; i < size; i++) {
370
-                Button button = mNewButtons.get(i);
371
-                MenuItem item = menu.add(Menu.NONE, button.getItemId(), size - i - 1, button.title);
372
-                item.setShowAsAction(getMenuItemShowAction(button.showAsAction));
373
-
374
-                // Set button icon
375
-                if (button.hasIcon()) {
376
-                    Drawable icon = icons.get(button.id);
377
-                    if (mTintColor != null) {
378
-                        ImageUtils.tint(icon, mTintColor);
379
-                    }
380
-                    item.setIcon(icon);
381
-                } else {
382
-                    textButtons.add(button.title);
383
-                }
384
-
385
-                // Disable button if needed
386
-                if (button.disabled) {
387
-                    item.setEnabled(false);
388
-                }
389
-            }
390
-
391
-            final RnnToolBar toolBar = mToolbarWR.get();
392
-            if (toolBar != null) {
393
-                // Tint overflow icon which appears when there's not enough space in Toolbar for icons
394
-                if (mTintColor != null) {
395
-                    ImageUtils.tint(toolBar.getOverflowIcon(), mTintColor);
396
-                }
397
-
398
-                // Tint text buttons
399
-                if (textButtons.size() > 0 && mButtonTextColor != null) {
400
-                    final View decorView = ((Activity) context).getWindow().getDecorView();
401
-                    decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
402
-                        @Override
403
-                        public void onGlobalLayout() {
404
-                            decorView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
405
-
406
-                            // Find TextViews
407
-                            for (String text : textButtons) {
408
-                                decorView.findViewsWithText(toolBar.mMenuItems, text, View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
409
-                            }
410
-
411
-                            // Set text color
412
-                            for (View button : toolBar.mMenuItems) {
413
-                                ((TextView) button).setTextColor(mButtonTextColor);
414
-                            }
415
-
416
-                            toolBar.mMenuItems.clear();
417
-                        }
418
-                    });
419
-                }
420
-
421
-                toolBar.mSetupToolbarTask = null;
422
-                mToolbarWR.clear();
423
-            }
424
-        }
425
-
426
-        private int getMenuItemShowAction(String action) {
427
-            switch (action) {
428
-                case "never":
429
-                    return MenuItem.SHOW_AS_ACTION_NEVER;
430
-                case "always":
431
-                    return MenuItem.SHOW_AS_ACTION_ALWAYS;
432
-                case "withText":
433
-                    return MenuItem.SHOW_AS_ACTION_WITH_TEXT;
434
-                default:
435
-                    return MenuItem.SHOW_AS_ACTION_IF_ROOM;
436
-            }
437
-        }
438
-    }
439
-}

+ 0
- 179
android/app/src/main/java/com/reactnativenavigation/views/ScreenStack.java View File

@@ -1,179 +0,0 @@
1
-package com.reactnativenavigation.views;
2
-
3
-import android.animation.LayoutTransition;
4
-import android.content.Context;
5
-import android.support.design.widget.CoordinatorLayout;
6
-import android.util.AttributeSet;
7
-import android.view.ViewGroup;
8
-
9
-import com.facebook.react.ReactInstanceManager;
10
-import com.reactnativenavigation.activities.BaseReactActivity;
11
-import com.reactnativenavigation.core.RctManager;
12
-import com.reactnativenavigation.core.objects.Screen;
13
-
14
-import java.util.Stack;
15
-
16
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
17
-
18
-public class ScreenStack extends android.support.design.widget.CoordinatorLayout {
19
-
20
-    private static final int DISAPPEAR_ANIMATION_DELAY = 200;
21
-
22
-    private static class ScreenView {
23
-        Screen screen;
24
-        RctView view;
25
-
26
-        public ScreenView(Screen screen, RctView view) {
27
-            this.screen = screen;
28
-            this.view = view;
29
-        }
30
-    }
31
-
32
-    private final Stack<ScreenView> mStack = new Stack<>();
33
-    private final ReactInstanceManager mReactInstanceManager = RctManager.getInstance().getReactInstanceManager();
34
-    private BaseReactActivity mReactActivity;
35
-
36
-    public ScreenStack(BaseReactActivity context) {
37
-        super(context);
38
-        init(context);
39
-    }
40
-
41
-    public ScreenStack(Context context, AttributeSet attrs) {
42
-        super(context, attrs);
43
-        init(context);
44
-    }
45
-
46
-    private void init(Context context) {
47
-        mReactActivity = (BaseReactActivity) context;
48
-        setLayoutTransition(new LayoutTransition());
49
-    }
50
-
51
-    public void push(Screen screen) {
52
-        push(screen, null);
53
-    }
54
-
55
-    public void push(Screen screen, RctView.OnDisplayedListener onDisplayed) {
56
-        RctView oldView = mStack.isEmpty() ? null : mStack.peek().view;
57
-        RctView view = new RctView(mReactActivity, mReactInstanceManager, screen, onDisplayed);
58
-        if (oldView != null) {
59
-            addView(view, MATCH_PARENT, MATCH_PARENT);
60
-
61
-            oldView.onTemporallyRemovedFromScreen();
62
-            getLayoutTransition().setStartDelay(LayoutTransition.DISAPPEARING, DISAPPEAR_ANIMATION_DELAY);
63
-            removeView(oldView);
64
-            getLayoutTransition().setStartDelay(LayoutTransition.DISAPPEARING, 0);
65
-        } else {
66
-            addView(view, MATCH_PARENT, MATCH_PARENT);
67
-        }
68
-        mStack.push(new ScreenView(screen, view));
69
-    }
70
-
71
-    public Screen pop() {
72
-        if (mStack.isEmpty() || getStackSize() == 1) {
73
-            return null;
74
-        }
75
-
76
-        ScreenView popped = mStack.pop();
77
-
78
-        RctView newView = mStack.peek().view;
79
-        addView(newView);
80
-        newView.onReAddToScreen();
81
-
82
-        popped.view.onRemoveFromScreen();
83
-        removeView(popped.view);
84
-        return popped.screen;
85
-    }
86
-
87
-    public Screen popToRoot() {
88
-        if (mStack.isEmpty() || getStackSize() <= 1) {
89
-            return null;
90
-        }
91
-
92
-        ScreenView oldScreenView = null;
93
-        while (getStackSize() > 1) {
94
-            ScreenView popped = mStack.pop();
95
-            popped.view.onRemoveFromScreen();
96
-            removeView(popped.view);
97
-            if (oldScreenView == null) {
98
-                oldScreenView = popped;
99
-            }
100
-        }
101
-
102
-        if (!mStack.isEmpty()) {
103
-            addView(mStack.peek().view, 0);
104
-        }
105
-
106
-        return oldScreenView != null ? oldScreenView.screen : null;
107
-    }
108
-
109
-    public Screen resetTo(Screen screen) {
110
-        return resetTo(screen, null);
111
-    }
112
-
113
-    public Screen resetTo(Screen screen, RctView.OnDisplayedListener onDisplayed) {
114
-        RctView view = new RctView(mReactActivity, mReactInstanceManager, screen, onDisplayed);
115
-        addView(view, MATCH_PARENT, MATCH_PARENT);
116
-
117
-        ScreenView oldScreenView = null;
118
-        if (!mStack.isEmpty()) {
119
-            while (getStackSize() > 0) {
120
-                ScreenView popped = mStack.pop();
121
-                popped.view.onRemoveFromScreen();
122
-                removeView(popped.view);
123
-                if (oldScreenView == null) {
124
-                    oldScreenView = popped;
125
-                }
126
-            }
127
-        }
128
-
129
-        // Add screen to stack after it's clear
130
-        mStack.push(new ScreenView(screen, view));
131
-
132
-        if (oldScreenView == null) {
133
-            return null;
134
-        }
135
-
136
-        return oldScreenView.screen;
137
-    }
138
-
139
-    public boolean isEmpty() {
140
-        return mStack.isEmpty();
141
-    }
142
-
143
-    public int getStackSize() {
144
-        return mStack.size();
145
-    }
146
-
147
-    public Screen peek() {
148
-        return mStack.peek().screen;
149
-    }
150
-
151
-    /**
152
-     * Remove the ScreenStack from {@code parent} while preventing all child react views from getting unmounted
153
-     */
154
-    public void removeFromScreen(ViewGroup parent) {
155
-        mStack.peek().view.onTemporallyRemovedFromScreen();
156
-
157
-        parent.removeView(this);
158
-    }
159
-
160
-    /**
161
-     * Add ScreenStack to {@code parent}
162
-     */
163
-    public void addToScreen(ViewGroup parent) {
164
-        mStack.peek().view.onReAddToScreen();
165
-
166
-        parent.addView(this, new CoordinatorLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
167
-    }
168
-
169
-    public void removeAllReactViews() {
170
-        while (!mStack.empty()) {
171
-            RctView view = mStack.pop().view;
172
-            // Ensure view will be properly detached and unmounted
173
-            view.onRemoveFromScreen();
174
-            // Unmount the view
175
-            view.detachReactRootView(mReactInstanceManager);
176
-        }
177
-        removeAllViews();
178
-    }
179
-}

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

@@ -0,0 +1,53 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.view.ViewGroup;
4
+import android.view.ViewTreeObserver;
5
+
6
+public class ScrollDirectionListener implements ViewTreeObserver.OnScrollChangedListener {
7
+    public enum Direction {
8
+        Up, Down
9
+    }
10
+
11
+    public interface OnScrollChanged {
12
+        void onScrollChanged(Direction direction);
13
+    }
14
+
15
+    private final ViewGroup view;
16
+    private OnScrollChanged onChanged;
17
+    private int lastScrollY = -1;
18
+
19
+    public ScrollDirectionListener(ViewGroup view, OnScrollChanged onChanged) {
20
+        this.view = view;
21
+        this.onChanged = onChanged;
22
+    }
23
+
24
+    @Override
25
+    public void onScrollChanged() {
26
+        if (!view.getViewTreeObserver().isAlive()) {
27
+            return;
28
+        }
29
+
30
+        final int scrollY = view.getScrollY();
31
+        if (isScrollPositionChanged(scrollY) && !isTopOverscroll(scrollY) && !isBottomOverscroll(scrollY)) {
32
+            onChanged.onScrollChanged(getScrollDirection(scrollY));
33
+            lastScrollY = scrollY;
34
+        }
35
+    }
36
+
37
+    private Direction getScrollDirection(int scrollY) {
38
+        return scrollY > lastScrollY ? Direction.Down : Direction.Up;
39
+    }
40
+
41
+    private boolean isBottomOverscroll(int scrollY) {
42
+        return scrollY >= (view.getChildAt(0).getHeight() - view.getHeight());
43
+    }
44
+
45
+    private boolean isTopOverscroll(int scrollY) {
46
+        return scrollY <= 0;
47
+    }
48
+
49
+    private boolean isScrollPositionChanged(int scrollY) {
50
+        return scrollY != lastScrollY;
51
+    }
52
+
53
+}

+ 74
- 0
android/app/src/main/java/com/reactnativenavigation/views/SideMenu.java View File

@@ -0,0 +1,74 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+import android.support.v4.widget.DrawerLayout;
5
+import android.view.Gravity;
6
+import android.widget.RelativeLayout;
7
+
8
+import com.reactnativenavigation.params.SideMenuParams;
9
+import com.reactnativenavigation.utils.ViewUtils;
10
+
11
+public class SideMenu extends DrawerLayout {
12
+
13
+    private ContentView sideMenuView;
14
+    private RelativeLayout contentContainer;
15
+
16
+    public RelativeLayout getContentContainer() {
17
+        return contentContainer;
18
+    }
19
+
20
+    public void destroy() {
21
+        sideMenuView.ensureUnmountOnDetachedFromWindow();
22
+        removeView(sideMenuView);
23
+    }
24
+
25
+    public void setVisible(boolean visible, boolean animated) {
26
+        if (!isShown() && visible) {
27
+            openDrawer(animated);
28
+        }
29
+
30
+        if (isShown() && !visible) {
31
+            closeDrawer(animated);
32
+        }
33
+    }
34
+
35
+    public void openDrawer() {
36
+        openDrawer(Gravity.LEFT);
37
+    }
38
+
39
+    public void openDrawer(boolean animated) {
40
+        openDrawer(Gravity.LEFT, animated);
41
+    }
42
+
43
+    public void closeDrawer(boolean animated) {
44
+        closeDrawer(Gravity.LEFT, animated);
45
+    }
46
+
47
+    public void toggleVisible(boolean animated) {
48
+        if (isShown()) {
49
+            closeDrawer(animated);
50
+        } else {
51
+            openDrawer(animated);
52
+        }
53
+    }
54
+
55
+    public SideMenu(Context context, SideMenuParams sideMenuParams) {
56
+        super(context);
57
+        createContentContainer();
58
+        createSideMenu(sideMenuParams);
59
+    }
60
+
61
+    private void createContentContainer() {
62
+        LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
63
+        contentContainer = new RelativeLayout(getContext());
64
+        contentContainer.setId(ViewUtils.generateViewId());
65
+        addView(contentContainer, lp);
66
+    }
67
+
68
+    private void createSideMenu(SideMenuParams sideMenuParams) {
69
+        sideMenuView = new ContentView(getContext(), sideMenuParams.screenId, sideMenuParams.navigationParams);
70
+        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
71
+        lp.gravity = Gravity.START;
72
+        addView(sideMenuView, lp);
73
+    }
74
+}

+ 130
- 0
android/app/src/main/java/com/reactnativenavigation/views/TitleBar.java View File

@@ -0,0 +1,130 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+import android.graphics.drawable.Drawable;
5
+import android.support.v7.widget.ActionMenuView;
6
+import android.support.v7.widget.Toolbar;
7
+import android.view.Menu;
8
+import android.view.View;
9
+
10
+import com.reactnativenavigation.animation.VisibilityAnimator;
11
+import com.reactnativenavigation.params.StyleParams;
12
+import com.reactnativenavigation.params.TitleBarButtonParams;
13
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
14
+import com.reactnativenavigation.utils.ViewUtils;
15
+
16
+import java.util.List;
17
+
18
+public class TitleBar extends Toolbar {
19
+
20
+    private boolean hideOnScroll = false;
21
+    private VisibilityAnimator visibilityAnimator;
22
+    private LeftButton leftButton;
23
+    private ActionMenuView actionMenuView;
24
+
25
+    public TitleBar(Context context) {
26
+        super(context);
27
+    }
28
+
29
+    @Override
30
+    public void onViewAdded(View child) {
31
+        super.onViewAdded(child);
32
+        if (child instanceof ActionMenuView) {
33
+            actionMenuView = (ActionMenuView) child;
34
+        }
35
+    }
36
+
37
+    public void setRightButtons(List<TitleBarButtonParams> rightButtons, String navigatorEventId) {
38
+        Menu menu = getMenu();
39
+        menu.clear();
40
+
41
+        if (rightButtons == null) {
42
+            return;
43
+        }
44
+
45
+        addButtonsToTitleBar(rightButtons, navigatorEventId, menu);
46
+    }
47
+
48
+    public void setLeftButton(TitleBarLeftButtonParams leftButtonParams,
49
+                              LeftButtonOnClickListener leftButtonOnClickListener, String navigatorEventId, boolean overrideBackPressInJs) {
50
+        if (shouldSetLeftButton(leftButtonParams)) {
51
+            createAndSetLeftButton(leftButtonParams, leftButtonOnClickListener, navigatorEventId, overrideBackPressInJs);
52
+        } else if (hasLeftButton()) {
53
+            updateLeftButton(leftButtonParams);
54
+        }
55
+    }
56
+
57
+    public void setStyle(StyleParams params) {
58
+        setVisibility(params.titleBarHidden ? GONE : VISIBLE);
59
+        setTitleTextColor(params);
60
+        colorOverflowButton(params);
61
+    }
62
+
63
+    private void colorOverflowButton(StyleParams params) {
64
+        Drawable overflowIcon = actionMenuView.getOverflowIcon();
65
+        if (shouldColorOverflowButton(params, overflowIcon)) {
66
+            ViewUtils.tintDrawable(overflowIcon, params.titleBarButtonColor.getColor(), true);
67
+        }
68
+    }
69
+
70
+    private boolean shouldColorOverflowButton(StyleParams params, Drawable overflowIcon) {
71
+        return overflowIcon != null && params.titleBarButtonColor.hasColor();
72
+    }
73
+
74
+    private void setTitleTextColor(StyleParams params) {
75
+        if (params.titleBarTitleColor.hasColor()) {
76
+            setTitleTextColor(params.titleBarTitleColor.getColor());
77
+        }
78
+    }
79
+
80
+    private void addButtonsToTitleBar(List<TitleBarButtonParams> rightButtons, String navigatorEventId, Menu menu) {
81
+        for (int i = 0; i < rightButtons.size(); i++) {
82
+            final TitleBarButton button = new TitleBarButton(menu, this, rightButtons.get(i), navigatorEventId);
83
+            addButtonInReverseOrder(rightButtons, i, button);
84
+        }
85
+    }
86
+
87
+    private void addButtonInReverseOrder(List<TitleBarButtonParams> rightButtons, int i, TitleBarButton button) {
88
+        final int index = rightButtons.size() - i - 1;
89
+        button.addToMenu(index);
90
+    }
91
+
92
+    private boolean hasLeftButton() {
93
+        return leftButton != null;
94
+    }
95
+
96
+    private void updateLeftButton(TitleBarLeftButtonParams leftButtonParams) {
97
+        leftButton.setIconState(leftButtonParams);
98
+    }
99
+
100
+    private boolean shouldSetLeftButton(TitleBarLeftButtonParams leftButtonParams) {
101
+        return leftButton == null && leftButtonParams != null;
102
+    }
103
+
104
+    private void createAndSetLeftButton(TitleBarLeftButtonParams leftButtonParams,
105
+                                        LeftButtonOnClickListener leftButtonOnClickListener,
106
+                                        String navigatorEventId,
107
+                                        boolean overrideBackPressInJs) {
108
+        leftButton = new LeftButton(getContext(), leftButtonParams, leftButtonOnClickListener, navigatorEventId,
109
+                overrideBackPressInJs);
110
+        setNavigationOnClickListener(leftButton);
111
+        setNavigationIcon(leftButton);
112
+    }
113
+
114
+    public void setHideOnScroll(boolean hideOnScroll) {
115
+        this.hideOnScroll = hideOnScroll;
116
+    }
117
+
118
+    public void onScrollChanged(ScrollDirectionListener.Direction direction) {
119
+        if (hideOnScroll) {
120
+            if (visibilityAnimator == null) {
121
+                createScrollAnimator();
122
+            }
123
+            visibilityAnimator.onScrollChanged(direction);
124
+        }
125
+    }
126
+
127
+    private void createScrollAnimator() {
128
+        visibilityAnimator = new VisibilityAnimator(this, VisibilityAnimator.HideDirection.Up, getHeight());
129
+    }
130
+}

+ 98
- 0
android/app/src/main/java/com/reactnativenavigation/views/TitleBarButton.java View File

@@ -0,0 +1,98 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.support.annotation.NonNull;
4
+import android.view.Menu;
5
+import android.view.MenuItem;
6
+import android.view.View;
7
+import android.widget.TextView;
8
+
9
+import com.reactnativenavigation.NavigationApplication;
10
+import com.reactnativenavigation.params.TitleBarButtonParams;
11
+import com.reactnativenavigation.utils.ViewUtils;
12
+
13
+import java.util.ArrayList;
14
+
15
+public class TitleBarButton implements MenuItem.OnMenuItemClickListener {
16
+
17
+    private final Menu menu;
18
+    private final View parent;
19
+    private TitleBarButtonParams buttonParams;
20
+    private String navigatorEventId;
21
+
22
+    public TitleBarButton(Menu menu, View parent, TitleBarButtonParams buttonParams, String navigatorEventId) {
23
+        this.menu = menu;
24
+        this.parent = parent;
25
+        this.buttonParams = buttonParams;
26
+        this.navigatorEventId = navigatorEventId;
27
+    }
28
+
29
+    public MenuItem addToMenu(int index) {
30
+        MenuItem item = menu.add(Menu.NONE, Menu.NONE, index, buttonParams.label);
31
+        item.setShowAsAction(buttonParams.showAsAction.action);
32
+        item.setEnabled(buttonParams.enabled);
33
+        setIcon(item);
34
+        setColor();
35
+        item.setOnMenuItemClickListener(this);
36
+        return item;
37
+    }
38
+
39
+
40
+    private void setIcon(MenuItem item) {
41
+        if (hasIcon()) {
42
+            item.setIcon(buttonParams.icon);
43
+        }
44
+    }
45
+
46
+    private void setColor() {
47
+        if (!hasColor()) {
48
+            return;
49
+        }
50
+
51
+        if (hasIcon()) {
52
+            setIconColor();
53
+        } else {
54
+            setTextColor();
55
+        }
56
+    }
57
+
58
+    private void setIconColor() {
59
+        ViewUtils.tintDrawable(buttonParams.icon, buttonParams.color.getColor(), buttonParams.enabled);
60
+    }
61
+
62
+    private void setTextColor() {
63
+        ViewUtils.runOnPreDraw(parent, new Runnable() {
64
+            @Override
65
+            public void run() {
66
+                ArrayList<View> outViews = findActualTextViewInMenuByLabel();
67
+                setTextColorForFoundButtonViews(outViews);
68
+            }
69
+        });
70
+    }
71
+
72
+    @NonNull
73
+    private ArrayList<View> findActualTextViewInMenuByLabel() {
74
+        ArrayList<View> outViews = new ArrayList<>();
75
+        parent.findViewsWithText(outViews, buttonParams.label, View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
76
+        return outViews;
77
+    }
78
+
79
+    private void setTextColorForFoundButtonViews(ArrayList<View> outViews) {
80
+        for (View button : outViews) {
81
+            ((TextView) button).setTextColor(buttonParams.color.getColor());
82
+        }
83
+    }
84
+
85
+    private boolean hasIcon() {
86
+        return buttonParams.icon != null;
87
+    }
88
+
89
+    private boolean hasColor() {
90
+        return buttonParams.color.hasColor();
91
+    }
92
+
93
+    @Override
94
+    public boolean onMenuItemClick(MenuItem item) {
95
+        NavigationApplication.instance.sendNavigatorEvent(buttonParams.eventId, navigatorEventId);
96
+        return true;
97
+    }
98
+}

+ 7
- 0
android/app/src/main/java/com/reactnativenavigation/views/TitleBarMenuButton.java View File

@@ -0,0 +1,7 @@
1
+package com.reactnativenavigation.views;
2
+
3
+public class TitleBarMenuButton {
4
+
5
+    public TitleBarMenuButton() {
6
+    }
7
+}

+ 74
- 0
android/app/src/main/java/com/reactnativenavigation/views/TopBar.java View File

@@ -0,0 +1,74 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+import android.support.design.widget.AppBarLayout;
5
+import android.support.design.widget.TabLayout;
6
+
7
+import com.reactnativenavigation.params.StyleParams;
8
+import com.reactnativenavigation.params.TitleBarButtonParams;
9
+import com.reactnativenavigation.params.TitleBarLeftButtonParams;
10
+import com.reactnativenavigation.utils.ViewUtils;
11
+
12
+import java.util.List;
13
+
14
+public class TopBar extends AppBarLayout {
15
+
16
+    private TitleBar titleBar;
17
+    private TopTabs topTabs;
18
+
19
+    public TopBar(Context context) {
20
+        super(context);
21
+        setFitsSystemWindows(true);
22
+        setId(ViewUtils.generateViewId());
23
+    }
24
+
25
+    public void addTitleBarAndSetButtons(List<TitleBarButtonParams> rightButtons,
26
+                                         TitleBarLeftButtonParams leftButton,
27
+                                         LeftButtonOnClickListener leftButtonOnClickListener,
28
+                                         String navigatorEventId, boolean overrideBackPressInJs) {
29
+        titleBar = new TitleBar(getContext());
30
+        addView(titleBar);
31
+        titleBar.setRightButtons(rightButtons, navigatorEventId);
32
+        titleBar.setLeftButton(leftButton, leftButtonOnClickListener, navigatorEventId, overrideBackPressInJs);
33
+    }
34
+
35
+    public void setTitle(String title) {
36
+        titleBar.setTitle(title);
37
+    }
38
+
39
+    public void setStyle(StyleParams styleParams) {
40
+        if (styleParams.topBarColor.hasColor()) {
41
+            setBackgroundColor(styleParams.topBarColor.getColor());
42
+        }
43
+        setVisibility(styleParams.topBarHidden ? GONE : VISIBLE);
44
+        titleBar.setStyle(styleParams);
45
+        setTopTabsStyle(styleParams);
46
+    }
47
+
48
+    public void setTitleBarRightButtons(String navigatorEventId, List<TitleBarButtonParams> titleBarButtons) {
49
+        titleBar.setRightButtons(titleBarButtons, navigatorEventId);
50
+    }
51
+
52
+    public TabLayout initTabs() {
53
+        topTabs = new TopTabs(getContext());
54
+        addView(topTabs);
55
+        return topTabs;
56
+    }
57
+
58
+    public void setTitleBarLeftButton(String navigatorEventId,
59
+                                      LeftButtonOnClickListener leftButtonOnClickListener,
60
+                                      TitleBarLeftButtonParams titleBarLeftButtonParams,
61
+                                      boolean overrideBackPressInJs) {
62
+        titleBar.setLeftButton(titleBarLeftButtonParams, leftButtonOnClickListener, navigatorEventId,
63
+                overrideBackPressInJs);
64
+    }
65
+
66
+    private void setTopTabsStyle(StyleParams style) {
67
+        if (topTabs == null) {
68
+            return;
69
+        }
70
+
71
+        topTabs.setTopTabsTextColor(style);
72
+        topTabs.setSelectedTabIndicatorStyle(style);
73
+    }
74
+}

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

@@ -0,0 +1,40 @@
1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+import android.content.res.ColorStateList;
5
+import android.support.design.widget.TabLayout;
6
+
7
+import com.reactnativenavigation.params.StyleParams;
8
+
9
+public class TopTabs extends TabLayout {
10
+
11
+    public TopTabs(Context context) {
12
+        super(context);
13
+    }
14
+
15
+    void setSelectedTabIndicatorStyle(StyleParams style) {
16
+        if (style.selectedTopTabIndicatorColor.hasColor()) {
17
+            setSelectedTabIndicatorColor(style.selectedTopTabIndicatorColor.getColor());
18
+        }
19
+
20
+        if (style.selectedTopTabIndicatorHeight >= 0) {
21
+            setSelectedTabIndicatorHeight(style.selectedTopTabIndicatorHeight);
22
+        }
23
+    }
24
+
25
+    void setTopTabsTextColor(StyleParams style) {
26
+        ColorStateList originalTabColors = getTabTextColors();
27
+        int selectedTabColor = originalTabColors != null ? originalTabColors.getColorForState(TabLayout.SELECTED_STATE_SET, -1) : -1;
28
+        int tabTextColor = originalTabColors != null ? originalTabColors.getColorForState(TabLayout.EMPTY_STATE_SET, -1) : -1;
29
+
30
+        if (style.topTabTextColor.hasColor()) {
31
+            tabTextColor = style.topTabTextColor.getColor();
32
+        }
33
+
34
+        if (style.selectedTopTabTextColor.hasColor()) {
35
+            selectedTabColor = style.selectedTopTabTextColor.getColor();
36
+        }
37
+
38
+        setTabTextColors(tabTextColor, selectedTabColor);
39
+    }
40
+}

+ 0
- 0
android/app/src/main/res/layout/bottom_tab_activity.xml View File


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