Browse Source

v5 (#5931)

Merge v5 into master

Highlights of this release
* Easier installation
* autolink and reac-native link support
* Shared Element Transition - reimplemented from scratch and new API
* [iOS] showModal animation api parity
* [Android] Animation values are now declared in dp
* [iOS] deprecate topBar.drawBehind
* [Android] RNN is migrating to Kotlin

closes #5904
Guy Carmeli 4 years ago
parent
commit
373f20b580
No account linked to committer's email address
100 changed files with 1995 additions and 1531 deletions
  1. 0
    10
      .github/_workflows/gradle-wrapper-validation.yml
  2. 0
    20
      CHANGELOG.md
  3. 39
    0
      Color+Interpolation.h
  4. 157
    0
      Color+Interpolation.m
  5. 48
    0
      LNInterpolable.h
  6. 2
    0
      ReactNativeNavigation.podspec
  7. 4
    0
      autolink/postlink.sh
  8. 50
    0
      autolink/postlink/activityLinker.js
  9. 78
    0
      autolink/postlink/appDelegateLinker.js
  10. 68
    0
      autolink/postlink/applicationLinker.js
  11. 94
    0
      autolink/postlink/gradleLinker.js
  12. 33
    0
      autolink/postlink/log.js
  13. 12
    0
      autolink/postlink/path.js
  14. 12
    0
      autolink/postlink/postLinkAndroid.js
  15. 7
    0
      autolink/postlink/postLinkIOS.js
  16. 8
    0
      autolink/postlink/run.js
  17. 14
    0
      autolink/postlink/stringUtils.js
  18. 5
    34
      docs/docs/Installing.md
  19. 64
    3
      lib/android/app/build.gradle
  20. 5
    4
      lib/android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java
  21. 4
    24
      lib/android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java
  22. 47
    41
      lib/android/app/src/main/java/com/reactnativenavigation/anim/NavigationAnimator.java
  23. 0
    119
      lib/android/app/src/main/java/com/reactnativenavigation/parse/AnimationOptions.java
  24. 111
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/AnimationOptions.kt
  25. 3
    3
      lib/android/app/src/main/java/com/reactnativenavigation/parse/AnimationsOptions.java
  26. 13
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/ElementTransitionOptions.kt
  27. 34
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/ElementTransitions.kt
  28. 24
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/FadeAnimation.java
  29. 11
    3
      lib/android/app/src/main/java/com/reactnativenavigation/parse/NestedAnimationsOptions.java
  30. 0
    5
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java
  31. 13
    18
      lib/android/app/src/main/java/com/reactnativenavigation/parse/SharedElementTransitionOptions.java
  32. 57
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/SharedElements.java
  33. 0
    49
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Transitions.java
  34. 0
    81
      lib/android/app/src/main/java/com/reactnativenavigation/parse/ValueAnimationOptions.java
  35. 88
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/ValueAnimationOptions.kt
  36. 6
    4
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/Presenter.java
  37. 0
    52
      lib/android/app/src/main/java/com/reactnativenavigation/react/ElementViewManager.java
  38. 10
    7
      lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java
  39. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationPackage.java
  40. 5
    26
      lib/android/app/src/main/java/com/reactnativenavigation/react/ReactGateway.java
  41. 9
    24
      lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java
  42. 4
    4
      lib/android/app/src/main/java/com/reactnativenavigation/utils/CollectionUtils.java
  43. 8
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/Context.kt
  44. 0
    140
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageLoader.java
  45. 132
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageLoader.kt
  46. 3
    2
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageLoadingListenerAdapter.java
  47. 3
    3
      lib/android/app/src/main/java/com/reactnativenavigation/utils/TextViewUtils.java
  48. 14
    2
      lib/android/app/src/main/java/com/reactnativenavigation/utils/UiUtils.java
  49. 5
    1
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ViewTags.java
  50. 14
    16
      lib/android/app/src/main/java/com/reactnativenavigation/utils/ViewUtils.java
  51. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ChildController.java
  52. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java
  53. 0
    5
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/IReactView.java
  54. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarButtonController.java
  55. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarReactViewController.java
  56. 23
    10
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java
  57. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/externalcomponent/ExternalComponentViewController.java
  58. 3
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalStack.java
  59. 0
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java
  60. 19
    23
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java
  61. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarBackgroundViewController.java
  62. 28
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontrolleroverlay/ViewControllerOverlay.kt
  63. 0
    8
      lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentLayout.java
  64. 3
    2
      lib/android/app/src/main/java/com/reactnativenavigation/views/Fab.java
  65. 0
    85
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/Element.java
  66. 19
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/ElementTransition.kt
  67. 0
    41
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/ElementTransitionManager.java
  68. 74
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/ElementTransitionManager.kt
  69. 42
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/SharedElementTransition.kt
  70. 13
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/Transition.kt
  71. 0
    52
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/TransitionAnimatorCreator.java
  72. 137
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/TransitionAnimatorCreator.kt
  73. 27
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/TransitionSet.kt
  74. 0
    15
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/TransitionValidator.java
  75. 0
    44
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/BackgroundColorAnimator.java
  76. 28
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/BackgroundColorAnimator.kt
  77. 15
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/BackgroundColorEvaluator.kt
  78. 0
    45
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/ClipBoundsEvaluator.java
  79. 0
    14
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/LabColorEvaluator.java
  80. 0
    63
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/MatrixAnimator.java
  81. 39
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/MatrixAnimator.kt
  82. 0
    63
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/PropertyAnimatorCreator.java
  83. 27
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/PropertyAnimatorCreator.kt
  84. 0
    39
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/ScaleXAnimator.java
  85. 22
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/ScaleXAnimator.kt
  86. 0
    39
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/ScaleYAnimator.java
  87. 22
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/ScaleYAnimator.kt
  88. 0
    35
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/TextChangeAnimator.java
  89. 43
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/TextChangeAnimator.kt
  90. 0
    35
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/TextColorAnimator.java
  91. 0
    33
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/TextSizeAnimator.java
  92. 0
    40
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/XAnimator.java
  93. 28
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/XAnimator.kt
  94. 0
    40
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/YAnimator.java
  95. 29
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/YAnimator.kt
  96. 6
    10
      lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBar.java
  97. 8
    0
      lib/android/app/src/main/res/values/ids.xml
  98. 4
    42
      lib/android/app/src/reactNative51/java/com/reactnativenavigation/react/NavigationReactNativeHost.java
  99. 5
    42
      lib/android/app/src/reactNative55/java/com/reactnativenavigation/react/NavigationReactNativeHost.java
  100. 0
    0
      lib/android/app/src/reactNative56/java/com/reactnativenavigation/react/NavigationReactNativeHost.java

+ 0
- 10
.github/_workflows/gradle-wrapper-validation.yml View File

@@ -1,10 +0,0 @@
1
-name: "Validate Gradle Wrapper"
2
-on: [push, pull_request]
3
-
4
-jobs:
5
-  validation:
6
-    name: "Validation"
7
-    runs-on: ubuntu-latest
8
-    steps:
9
-      - uses: actions/checkout@v2
10
-      - uses: gradle/wrapper-validation-action@v1

+ 0
- 20
CHANGELOG.md View File

@@ -1,24 +1,4 @@
1 1
 # Changelog
2
-# 4.8.1
3
-## Fixed
4
-### Android
5
-* Fix NPE when showing Overlay [#bfde34a](https://github.com/wix/react-native-navigation/commit/bfde34a5862c971587583aa52cb450cf526d5c66) by [guyca](https://github.com/guyca)
6
-### iOS
7
-* Fix overlays touch interception on new iPads [#433f48b](https://github.com/wix/react-native-navigation/commit/433f48b59d9be5aa328361654341afa8414c2e21) by [yogevbd](https://github.com/yogevbd)
8
-
9
-# 4.8.0
10
-## Fixed
11
-### Android
12
-* Support react-native-youtube [#ffbd288](https://github.com/wix/react-native-navigation/commit/ffbd2882b24109ff8f2b51ca3c8c88822cc9afb7) by [guyca](https://github.com/guyca)
13
-### iOS
14
-* Fix wrong SafeAreaView margins when using bottomTabs.drawBehind: true [#527fd49](https://github.com/wix/react-native-navigation/commit/527fd49f2f1517143032a8b14f6ab17d2f74c032) by [yogevbd](https://github.com/yogevbd)
15
-
16
-# 4.7.1
17
-## Fixed
18
-* Move selectTabOnPress prop to correct interface [#d6ead65](https://github.com/wix/react-native-navigation/commit/d6ead65c9331f12d3f79fc3b5bb7e0a0de80816e) by [phanghos](https://github.com/phanghos)
19
-### iOS
20
-* Fix external components layout measurement [#1961181](https://github.com/wix/react-native-navigation/commit/196118186fb788200dafcc1e11cd9f7d6e3f6dda) by [yogevbd](https://github.com/yogevbd)
21
-
22 2
 # 4.7.0
23 3
 ## Added
24 4
 * On tab press event - handle tab selection in Js [#b153142](https://github.com/wix/react-native-navigation/commit/b1531428a0a9608b5d1c84547f228d5de0c1aca2) by [pontusab](https://github.com/pontusab)

+ 39
- 0
Color+Interpolation.h View File

@@ -0,0 +1,39 @@
1
+//
2
+//  Color+Interpolation.h
3
+//
4
+//  Created by Leo Natan on 01/10/2016.
5
+//  Copyright © 2016 Leo Natan. All rights reserved.
6
+//
7
+
8
+#if __has_include(<UIKit/UIKit.h>) || __has_include(<AppKit/AppKit.h>)
9
+
10
+#import "LNInterpolable.h"
11
+
12
+#if __has_include(<UIKit/UIKit.h>)
13
+#import <UIKit/UIKit.h>
14
+#else
15
+#import <AppKit/AppKit.h>
16
+#endif
17
+
18
+/**
19
+ Interpolate using the LAB color space for optimal quality. This constant is equal to @c LNUseDefaultInterpolationBehavior.
20
+ */
21
+extern LNInterpolationBehavior const LNInterpolationBehaviorUseLABColorSpace;
22
+
23
+/**
24
+ Interpolate using the RGB color space.
25
+ */
26
+extern LNInterpolationBehavior const LNInterpolationBehaviorUseRGBColorSpace;
27
+
28
+/**
29
+ Interpolates between colors.
30
+ 
31
+ By default, colors are interpolated in the Lab color space for optimal quality at the expense of some performance. Use @c LNUseRGBInterpolationBehavior for better performance but suboptimal quality.
32
+ */
33
+#if __has_include(<UIKit/UIKit.h>)
34
+@interface UIColor (LNInterpolation) <LNInterpolable> @end
35
+#else
36
+@interface NSColor (LNInterpolation) <LNInterpolable> @end
37
+#endif
38
+
39
+#endif

+ 157
- 0
Color+Interpolation.m View File

@@ -0,0 +1,157 @@
1
+//
2
+//  Color+Interpolation.m
3
+//
4
+//  Created by Leo Natan on 01/10/2016.
5
+//  Copyright © 2016 Leo Natan. All rights reserved.
6
+//
7
+
8
+#if __has_include(<UIKit/UIKit.h>) || __has_include(<AppKit/AppKit.h>)
9
+
10
+#import "Color+Interpolation.h"
11
+
12
+#if __has_include(<UIKit/UIKit.h>)
13
+#define Color UIColor
14
+#else
15
+#define Color NSColor
16
+#endif
17
+
18
+#define SWAP(x, y) do { __typeof(x) __ZZZZ__SWAP = x; x = y; y = __ZZZZ__SWAP; } while(0)
19
+
20
+//Same value as LNInterpolationBehaviorUseDefault
21
+LNInterpolationBehavior const LNInterpolationBehaviorUseLABColorSpace = @"LNInterpolationBehaviorUseDefault";
22
+LNInterpolationBehavior const LNInterpolationBehaviorUseRGBColorSpace = @"LNInterpolationBehaviorUseRGB";
23
+
24
+extern double LNLinearInterpolate(double from, double to, double p);
25
+
26
+static NSArray<NSNumber*>* LNRGBComponentsFromColor(Color* color)
27
+{
28
+	size_t numberOfComponents = CGColorGetNumberOfComponents(color.CGColor);
29
+	const CGFloat* components = CGColorGetComponents(color.CGColor);
30
+	
31
+	return numberOfComponents == 2 ? @[@(components[0]), @(components[0]), @(components[0]), @(components[1])] : @[@(components[0]), @(components[1]), @(components[2]), @(components[3])];
32
+}
33
+
34
+static Color* LNColorFromRGBComponents(NSArray<NSNumber*>* components)
35
+{
36
+	return [Color colorWithRed:components[0].doubleValue green:components[1].doubleValue blue:components[2].doubleValue alpha:components[3].doubleValue];
37
+}
38
+
39
+static NSArray<NSNumber*>* LNLabComponentsFromColor(Color* color)
40
+{
41
+	NSArray<NSNumber*>* rgbComponents = LNRGBComponentsFromColor(color);
42
+	CGFloat r = rgbComponents[0].doubleValue;
43
+	CGFloat g = rgbComponents[1].doubleValue;
44
+	CGFloat b = rgbComponents[2].doubleValue;
45
+	
46
+	//RGB -> XYZ
47
+	//http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
48
+
49
+	r = (r > 0.04045) ? pow((r + 0.055) / 1.055, 2.4) : (r / 12.92);
50
+	g = (g > 0.04045) ? pow((g + 0.055) / 1.055, 2.4) : (g / 12.92);
51
+	b = (b > 0.04045) ? pow((b + 0.055) / 1.055, 2.4) : (b / 12.92);
52
+	
53
+	//http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html (sRGB -> XYZ)
54
+	CGFloat x = r * 41.24564 + g * 35.75761 + b * 18.04375;
55
+	CGFloat y = r * 21.26729 + g * 71.51522 + b * 07.21750;
56
+	CGFloat z = r * 01.93339 + g * 11.91920 + b * 95.03041;
57
+	
58
+	//XYZ -> Lab
59
+	//http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html
60
+	
61
+	static const CGFloat eps = 216.0 / 24389.0;
62
+	static const CGFloat k = 24389.0 / 27.0;
63
+	
64
+	x = x / 100.0;//95.047;
65
+	y = y / 100.0;//100.000;
66
+	z = z / 100.0;//108.883;
67
+	
68
+	x = x > eps ? pow(x, 1.0 / 3.0) : (k * x + 16.0) / 116.0;
69
+	y = y > eps ? pow(y, 1.0 / 3.0) : (k * y + 16.0) / 116.0;
70
+	z = z > eps ? pow(z, 1.0 / 3.0) : (k * z + 16.0) / 116.0;
71
+	
72
+	CGFloat l = 116 * y - 16;
73
+	CGFloat a = 500 * (x - y);
74
+	        b = 200 * (y - z);
75
+	
76
+	return @[@(l), @(a), @(b), rgbComponents[3]];
77
+}
78
+
79
+static Color* LNColorFromLabComponents(NSArray<NSNumber*>* components)
80
+{
81
+	CGFloat l = components[0].doubleValue;
82
+	CGFloat a = components[1].doubleValue;
83
+	CGFloat b = components[2].doubleValue;
84
+	
85
+	//Lab -> XYZ
86
+	//http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
87
+	
88
+	static const CGFloat eps = 216.0 / 24389.0;
89
+	static const CGFloat k = 24389.0 / 27.0;
90
+	
91
+	CGFloat y = (l + 16.0) / 116.0;
92
+	CGFloat x = a / 500 + y;
93
+	CGFloat z = y - b / 200;
94
+
95
+	x = pow(x, 3.0) > eps ? pow(x, 3.0) : (116 * x - 16) / k;
96
+	y = l > k * eps ? pow((l + 16) / 116, 3.0) : l / k;
97
+	z = pow(z, 3.0) > eps ? pow(z, 3.0) : (116 * z - 16) / k;
98
+	
99
+	x = x * 1.0;//.95047;
100
+	y = y * 1.0;//1.00000;
101
+	z = z * 1.0;//1.08883;
102
+	
103
+	//XYZ -> RGB
104
+	
105
+	//http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html (XYZ -> sRGB)
106
+	CGFloat r = x *  3.2404542 + y * -1.5371385 + z * -0.4985314;
107
+	CGFloat g = x * -0.9692660 + y *  1.8760108 + z *  0.0415560;
108
+	        b = x *  0.0556434 + y * -0.2040259 + z *  1.0572252;
109
+	
110
+	r = r <= 0.0031308 ? 12.92 * r : 1.055 * pow(r, 1.0 / 2.4) - 0.055;
111
+	g = g <= 0.0031308 ? 12.92 * g : 1.055 * pow(g, 1.0 / 2.4) - 0.055;
112
+	b = b <= 0.0031308 ? 12.92 * b : 1.055 * pow(b, 1.0 / 2.4) - 0.055;
113
+	
114
+	// return Color
115
+	return LNColorFromRGBComponents(@[@(r), @(g), @(b), components[3]]);
116
+}
117
+
118
+static inline __attribute__((__always_inline__)) Color* LNInterpolateColor(Color* fromValue, Color* toValue, CGFloat p, NSArray* (*compConverter)(Color*), Color* (*colorConverter)(NSArray*))
119
+{
120
+	NSArray<NSNumber*>* arrayC1 = compConverter(fromValue);
121
+	NSArray<NSNumber*>* arrayC2 = compConverter(toValue);
122
+	
123
+	NSMutableArray<NSNumber*>* arrayOutput = [NSMutableArray new];
124
+	[arrayC1 enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
125
+		arrayOutput[idx] = @(LNLinearInterpolate(obj.doubleValue, arrayC2[idx].doubleValue, p));
126
+	}];
127
+	
128
+	return colorConverter(arrayOutput);
129
+}
130
+
131
+@implementation Color (Interpolation)
132
+
133
+- (instancetype)interpolateToValue:(Color*)toValue progress:(double)p
134
+{
135
+	return [self interpolateToValue:toValue progress:p behavior:LNInterpolationBehaviorUseDefault];
136
+}
137
+
138
+- (instancetype)interpolateToValue:(id)toValue progress:(double)p behavior:(LNInterpolationBehavior)behavior
139
+{
140
+	NSParameterAssert([toValue isKindOfClass:[Color class]]);
141
+	
142
+	if(p <= 0)
143
+	{
144
+		return self;
145
+	}
146
+	
147
+	if(p >= 1)
148
+	{
149
+		return toValue;
150
+	}
151
+	
152
+	return LNInterpolateColor(self, toValue, p, behavior == LNInterpolationBehaviorUseRGBColorSpace ? LNRGBComponentsFromColor : LNLabComponentsFromColor, behavior == LNInterpolationBehaviorUseRGBColorSpace ? LNColorFromRGBComponents : LNColorFromLabComponents);
153
+}
154
+
155
+@end
156
+
157
+#endif

+ 48
- 0
LNInterpolable.h View File

@@ -0,0 +1,48 @@
1
+//
2
+//  LNInterpolable.h
3
+//
4
+//  Created by Leo Natan on 01/10/2016.
5
+//  Copyright © 2016 Leo Natan. All rights reserved.
6
+//
7
+
8
+#import <Foundation/Foundation.h>
9
+
10
+NS_ASSUME_NONNULL_BEGIN
11
+
12
+NS_SWIFT_NAME(InterpolationBehavior)
13
+typedef const NSString* LNInterpolationBehavior NS_TYPED_EXTENSIBLE_ENUM;
14
+
15
+/**
16
+ Interpolate using the default behavor of each implementation.
17
+ */
18
+extern LNInterpolationBehavior const LNInterpolationBehaviorUseDefault;
19
+
20
+
21
+/**
22
+ Classes implementing this protocol support interpolation.
23
+ */
24
+NS_SWIFT_NAME(Interpolable)
25
+@protocol LNInterpolable <NSObject>
26
+
27
+/**
28
+ Interpolates between @c self and @c toValue accodring to @c progress using the default behavior.
29
+
30
+ @param toValue The value to interpolate to
31
+ @param progress The progress of the interpolation
32
+ @return An object representing the interpolated value at the requested progress
33
+ */
34
+- (instancetype)interpolateToValue:(id)toValue progress:(double)progress NS_SWIFT_NAME(interpolate(to:progress:));
35
+
36
+/**
37
+ Interpolates between @c self and @c toValue according to @c progress using @c behavior.
38
+
39
+ @param toValue The value to interpolate to
40
+ @param behavior The bahvior to use for interpolation
41
+ @param progress The progress of the interpolation
42
+ @return An object representing the interpolated value at the requested progress
43
+ */
44
+- (instancetype)interpolateToValue:(id)toValue progress:(double)progress behavior:(LNInterpolationBehavior)behavior NS_SWIFT_NAME(interpolate(to:progress:behavior:));
45
+
46
+NS_ASSUME_NONNULL_END
47
+
48
+@end

+ 2
- 0
ReactNativeNavigation.podspec View File

@@ -19,5 +19,7 @@ Pod::Spec.new do |s|
19 19
   s.exclude_files  = "lib/ios/ReactNativeNavigationTests/**/*.*", "lib/ios/OCMock/**/*.*"
20 20
 
21 21
   s.dependency 'React'
22
+  s.dependency 'React-RCTImage'
23
+  s.dependency 'React-RCTText'
22 24
   s.frameworks = 'UIKit'
23 25
 end

+ 4
- 0
autolink/postlink.sh View File

@@ -0,0 +1,4 @@
1
+#!/usr/bin/env bash
2
+set -e
3
+
4
+echo "RNN postlink !!!!!!!!!!!!!!!!!"

+ 50
- 0
autolink/postlink/activityLinker.js View File

@@ -0,0 +1,50 @@
1
+// @ts-check
2
+var path = require('./path');
3
+var fs = require("fs");
4
+var { warnn, logn, infon, debugn } = require("./log");
5
+
6
+class ActivityLinker {
7
+  constructor() {
8
+    this.activityPath = path.mainActivityJava;
9
+  }
10
+
11
+  link() {
12
+    logn("Linking MainActivity...");
13
+    if (this.activityPath) {
14
+      var activityContent = fs.readFileSync(this.activityPath, "utf8");
15
+      activityContent = this._extendNavigationActivity(activityContent);
16
+      activityContent = this._removeGetMainComponentName(activityContent);
17
+      fs.writeFileSync(this.activityPath, activityContent);
18
+      infon("MainActivity linked successfully!\n");
19
+    } else {
20
+      warnn("   MainActivity not found!");
21
+    }
22
+  }
23
+
24
+  _removeGetMainComponentName(contents) {
25
+    var match = /\/\*\*\s*\n([^\*]|(\*(?!\/)))*\*\/\s*@Override\s*protected\s*String\s*getMainComponentName\s*\(\)\s*{\s*return.+\s*\}/.exec(contents);
26
+    if (match) {
27
+      debugn("   Removing getMainComponentName function");
28
+      return contents.replace(/\/\*\*\s*\n([^\*]|(\*(?!\/)))*\*\/\s*@Override\s*protected\s*String\s*getMainComponentName\s*\(\)\s*{\s*return.+\s*\}/, "");
29
+    }
30
+    return contents;
31
+  }
32
+
33
+  _extendNavigationActivity(activityContent) {
34
+    if (this._doesActivityExtendReactActivity(activityContent)) {
35
+      debugn("   Extending NavigationActivity")
36
+      return activityContent
37
+        .replace(/extends\s+ReactActivity\s*/, "extends NavigationActivity ")
38
+        .replace("import com.facebook.react.ReactActivity;", "import com.reactnativenavigation.NavigationActivity;")
39
+    } else {
40
+      warnn("   MainActivity already extends NavigationActivity")
41
+    }
42
+    return activityContent;
43
+  }
44
+
45
+  _doesActivityExtendReactActivity(activityContent) {
46
+    return /public\s+class\s+MainActivity\s+extends\s+ReactActivity\s*/.test(activityContent);
47
+  }
48
+}
49
+
50
+module.exports = ActivityLinker;

+ 78
- 0
autolink/postlink/appDelegateLinker.js View File

@@ -0,0 +1,78 @@
1
+// @ts-check
2
+var path = require('./path');
3
+var fs = require("fs");
4
+var {warnn, logn, infon, debugn} = require("./log");
5
+
6
+class ApplicationLinker {
7
+  constructor() {
8
+    this.appDelegatePath = path.appDelegate;
9
+  }
10
+
11
+  link() {
12
+    if (this.appDelegatePath) {
13
+      logn("Linking AppDelegate...");
14
+      var appDelegateContents = fs.readFileSync(this.appDelegatePath, "utf8");
15
+      if (this._doesBootstrapNavigation(appDelegateContents)) {
16
+        infon("AppDelegate already linked\n");
17
+        return;
18
+      }
19
+      appDelegateContents = this._removeUnneededImports(appDelegateContents);
20
+      appDelegateContents = this._importNavigation(appDelegateContents);
21
+      appDelegateContents = this._bootstrapNavigation(appDelegateContents);
22
+      appDelegateContents = this._removeApplicationLaunchContent(appDelegateContents);
23
+      fs.writeFileSync(this.appDelegatePath, appDelegateContents);
24
+      infon("AppDelegate linked successfully!\n");
25
+    }
26
+  }
27
+
28
+  _doesBootstrapNavigation(applicationContent) {
29
+    return /ReactNativeNavigation\s+bootstrap/.test(applicationContent);
30
+  }
31
+
32
+  _removeUnneededImports(applicationContent) {
33
+    const unneededImports = [/\#import\s+\<React\/RCTBridge.h>\s/, /#import\s+\<React\/RCTRootView.h>\s/];
34
+    debugn("   Removing Unneeded imports");
35
+    unneededImports.forEach(unneededImport => {
36
+      if (unneededImport.test(applicationContent)) {
37
+        applicationContent = applicationContent.replace(unneededImport, "");
38
+      }
39
+    });
40
+
41
+    return applicationContent;
42
+  }
43
+
44
+  _importNavigation(applicationContent) {
45
+    if (!this._doesImportNavigation(applicationContent)) {
46
+      debugn("   ");
47
+      return applicationContent
48
+        .replace(/#import\s+"AppDelegate.h"/, "#import \"AppDelegate.h\"\n#import <ReactNativeNavigation/ReactNativeNavigation.h>")
49
+    }
50
+    warnn("   AppDelegate already imports Navigation");
51
+    return applicationContent;
52
+  }
53
+
54
+  _removeApplicationLaunchContent(applicationContent) {
55
+    const toRemove = [/RCTRootView\s+\*rootView((.|\r|\s)*?)];\s+/, /rootView.backgroundColor((.|\r)*)];\s+/,
56
+      /self.window((.|\r)*)];\s+/, /UIViewController\s\*rootViewController((.|\r)*)];\s+/, /rootViewController\.view\s+=\s+rootView;\s+/,
57
+      /self.window.rootViewController\s+=\s+rootViewController;\s+/, /\[self.window\s+makeKeyAndVisible];\s+/
58
+    ]
59
+
60
+    toRemove.forEach(element => {
61
+      if (element.test(applicationContent)) {
62
+        applicationContent = applicationContent.replace(element, "");
63
+      }
64
+    });
65
+
66
+    return applicationContent;
67
+  }
68
+
69
+  _bootstrapNavigation(applicationContent) {
70
+    return applicationContent.replace(/RCTBridge.*];/, "[ReactNativeNavigation bootstrapWithDelegate:self launchOptions:launchOptions];");
71
+  }
72
+
73
+  _doesImportNavigation(applicationContent) {
74
+    return /#import\s+\<ReactNativeNavigation\/ReactNativeNavigation.h>/.test(applicationContent);
75
+  }
76
+}
77
+
78
+module.exports = ApplicationLinker;

+ 68
- 0
autolink/postlink/applicationLinker.js View File

@@ -0,0 +1,68 @@
1
+// @ts-check
2
+var path = require('./path');
3
+var fs = require("fs");
4
+var { warnn, logn, infon, debugn } = require("./log");
5
+
6
+class ApplicationLinker {
7
+  constructor() {
8
+    this.applicationPath = path.mainApplicationJava;
9
+  }
10
+
11
+  link() {
12
+    if (this.applicationPath) {
13
+      logn("Linking MainApplication...");
14
+      var applicationContents = fs.readFileSync(this.applicationPath, "utf8");
15
+      var linkers = [this._extendNavigationApplication, this._extendNavigationHost, this._removeSOLoaderInit];
16
+      applicationContents = this._extendNavigationApplication(applicationContents);
17
+      applicationContents = this._extendNavigationHost(applicationContents);
18
+      applicationContents = this._removeSOLoaderInit(applicationContents);
19
+      fs.writeFileSync(this.applicationPath, applicationContents);
20
+      infon("MainApplication linked successfully!\n");
21
+    }
22
+  }
23
+
24
+  _extendNavigationApplication(applicationContent) {
25
+    if (this._doesExtendApplication(applicationContent)) {
26
+      debugn("   Extending NavigationApplication");
27
+      return applicationContent
28
+        .replace(/extends\s+Application\s+implements\s+ReactApplication/gi, "extends NavigationApplication")
29
+        .replace("import com.facebook.react.ReactApplication;", "import com.reactnativenavigation.NavigationApplication;");
30
+    }
31
+    warnn("   MainApplication already extends NavigationApplication");
32
+    return applicationContent;
33
+  }
34
+
35
+  _doesExtendApplication(applicationContent) {
36
+    return /\s+MainApplication\s+extends\s+Application\s+implements\s+ReactApplication\s+/.test(applicationContent);
37
+  }
38
+
39
+  _extendNavigationHost(applicationContent) {
40
+    if (this._doesExtendReactNativeHost(applicationContent)) {
41
+      debugn("   Changing host implementation to NavigationReactNativeHost");
42
+      return applicationContent
43
+        .replace("new ReactNativeHost(this)", "new NavigationReactNativeHost(this)")
44
+        .replace("import com.facebook.react.ReactNativeHost;", "import com.facebook.react.ReactNativeHost;\nimport com.reactnativenavigation.react.NavigationReactNativeHost;")
45
+    }
46
+    warnn("   NavigationReactNativeHost is already used");
47
+    return applicationContent;
48
+  }
49
+
50
+  _removeSOLoaderInit(applicationContent) {
51
+    if (this._isSOLoaderInitCalled(applicationContent)) {
52
+      debugn("   Removing call to SOLoader.init()");
53
+      return applicationContent.replace(/SoLoader.init\(\s*this\s*,\s*[/* native exopackage */]*\s*false\s*\);/, "")
54
+    }
55
+    warnn("   SOLoader.init() is not called");
56
+    return applicationContent;
57
+  }
58
+
59
+  _isSOLoaderInitCalled(applicationContent) {
60
+    return /SoLoader.init\(this,\s*[/* native exopackage */]*\s*false\);/.test(applicationContent);
61
+  }
62
+
63
+  _doesExtendReactNativeHost(applicationContent) {
64
+    return /\s*new ReactNativeHost\(this\)\s*/.test(applicationContent);
65
+  }
66
+}
67
+
68
+module.exports = ApplicationLinker;

+ 94
- 0
autolink/postlink/gradleLinker.js View File

@@ -0,0 +1,94 @@
1
+// @ts-check
2
+var path = require('./path');
3
+var fs = require('fs');
4
+var { warnn, errorn, logn, infon, debugn } = require('./log');
5
+var { insertString } = require('./stringUtils');
6
+var DEFAULT_KOTLIN_VERSION = '1.3.61';
7
+
8
+class GradleLinker {
9
+  constructor() {
10
+    this.gradlePath = path.rootGradle;
11
+  }
12
+
13
+  link() {
14
+    logn('Linking root build.gradle...');
15
+    if (this.gradlePath) {
16
+      var contents = fs.readFileSync(this.gradlePath, 'utf8');
17
+      contents = this._setKotlinVersion(contents);
18
+      contents = this._setKotlinPluginDependency(contents);
19
+      fs.writeFileSync(this.gradlePath, contents);
20
+      infon('Root build.gradle linked successfully!\n');
21
+    } else {
22
+      warnn('   Root build.gradle not found!');
23
+    }
24
+  }
25
+
26
+  _setKotlinPluginDependency(contents) {
27
+    if (this._isKotlinPluginDeclared(contents)) {
28
+      warnn('   Kotlin plugin already declared')
29
+      return contents;
30
+    }
31
+    var match = /classpath\s*\(*["']com\.android\.tools\.build:gradle:/.exec(contents);
32
+    if (match) {
33
+      debugn("   Adding Kotlin plugin");
34
+      return insertString(contents, match.index, `classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${DEFAULT_KOTLIN_VERSION}"\n        `);
35
+    } else {
36
+      errorn("   Could not add kotlin plugin dependency")
37
+    }
38
+    return contents;
39
+  }
40
+
41
+  _setKotlinVersion(contents) {
42
+    if (this._isKotlinVersionSpecified(contents)) {
43
+      warnn("   Kotlin version already specified");
44
+    } else {
45
+      var kotlinVersion = this._getKotlinVersion(contents);
46
+      if (this._hasExtensionVariablesBlock(contents)) {
47
+        debugn("   Adding RNNKotlinVersion to extension block");
48
+        return contents.replace(/ext\s*{/, `ext {\n        RNNKotlinVersion = ${kotlinVersion}`);
49
+      } else {
50
+        debugn("   Adding RNNKotlinVersion extension variable");
51
+        return contents.replace(/buildscript\s*{/, `buildscript {\n    ext.RNNKotlinVersion = ${kotlinVersion}`);
52
+      }
53
+    }
54
+    return contents;
55
+  }
56
+
57
+  /**
58
+   * @param { string } contents
59
+   */
60
+  _getKotlinVersion(contents) {
61
+    var hardCodedVersion = contents.match(/(?<=kotlin-gradle-plugin:)\$*[\d\.]{3,}/);
62
+    if (hardCodedVersion && hardCodedVersion.length > 0) {
63
+      return `"${hardCodedVersion[0]}"`;
64
+    }
65
+    var extensionVariableVersion = contents.match(/(?<=kotlin-gradle-plugin:)\$*[a-zA-Z\d\.]*/);
66
+    if (extensionVariableVersion && extensionVariableVersion.length > 0) {
67
+      return extensionVariableVersion[0].replace("$", "");
68
+    }
69
+    return `"${DEFAULT_KOTLIN_VERSION}"`;
70
+  }
71
+
72
+  /**
73
+   * @param {string} contents
74
+   */
75
+  _hasExtensionVariablesBlock(contents) {
76
+    return /ext\s*{/.test(contents);
77
+  }
78
+
79
+  /**
80
+   * @param {string} contents
81
+   */
82
+  _isKotlinVersionSpecified(contents) {
83
+    return /RNNKotlinVersion/.test(contents);
84
+  }
85
+
86
+  /**
87
+   * @param {string} contents
88
+   */
89
+  _isKotlinPluginDeclared(contents) {
90
+    return /org.jetbrains.kotlin:kotlin-gradle-plugin:/.test(contents);
91
+  }
92
+}
93
+
94
+module.exports = GradleLinker;

+ 33
- 0
autolink/postlink/log.js View File

@@ -0,0 +1,33 @@
1
+const bColors = {
2
+  HEADER    : '\033[95m',
3
+  OKBLUE    : '\033[94m',
4
+  OKGREEN   : '\033[92m',
5
+  WARNING   : '\033[93m',
6
+  FAIL      : '\033[91m',
7
+  ENDC      : '\033[0m', 
8
+  BOLD      : '\033[1m',   
9
+  UNDERLINE : '\033[4m'
10
+}
11
+
12
+const log = (text) => process.stdout.write(text);
13
+const logn = (text) => process.stdout.write(text + "\n");
14
+const warn = (text) => process.stdout.write(`${bColors.WARNING}${text}${bColors.ENDC}`)
15
+const warnn = (text) => warn(text + "\n");
16
+const error = (text) => process.stdout.write(`${bColors.FAIL}${text}${bColors.ENDC}`)
17
+const errorn = (text) => error(text + "\n");
18
+const info = (text) => process.stdout.write(`${bColors.OKGREEN}${text}${bColors.ENDC}`)
19
+const infon = (text) => info(text + "\n");
20
+const debug = (text) => process.stdout.write(`${bColors.OKBLUE}${text}${bColors.ENDC}`)
21
+const debugn = (text) => debug(text + "\n");
22
+
23
+module.exports = {
24
+  log,
25
+  logn,
26
+  warn,
27
+  warnn,
28
+  info,
29
+  infon,
30
+  debug,
31
+  debugn,
32
+  errorn
33
+}

+ 12
- 0
autolink/postlink/path.js View File

@@ -0,0 +1,12 @@
1
+var glob = require("glob");
2
+var ignoreFolders = { ignore: ["node_modules/**", "**/build/**"] };
3
+
4
+var manifestPath = glob.sync("**/AndroidManifest.xml", ignoreFolders)[0];
5
+
6
+exports.mainActivityJava = glob.sync("**/MainActivity.java", ignoreFolders)[0];
7
+exports.mainActivityKotlin = glob.sync("**/MainActivity.kt", ignoreFolders)[0];
8
+var mainApplicationJava = glob.sync("**/MainApplication.java", ignoreFolders)[0];
9
+exports.mainApplicationJava = mainApplicationJava;
10
+exports.rootGradle = mainApplicationJava.replace(/android\/app\/.*\.java/, "android/build.gradle");;
11
+
12
+exports.appDelegate = glob.sync("**/AppDelegate.m", ignoreFolders)[0];

+ 12
- 0
autolink/postlink/postLinkAndroid.js View File

@@ -0,0 +1,12 @@
1
+// @ts-check
2
+var { infon } = require("./log");
3
+var AppLinker = require("./applicationLinker");
4
+var ActivityLinker = require("./activityLinker");
5
+var GradleLinker = require("./gradleLinker");
6
+
7
+module.exports = () => {
8
+  console.log("Running android postlink script\n");
9
+  new AppLinker().link();
10
+  new ActivityLinker().link();
11
+  new GradleLinker().link();
12
+}

+ 7
- 0
autolink/postlink/postLinkIOS.js View File

@@ -0,0 +1,7 @@
1
+// @ts-check
2
+var AppDelegateLinker = require("./appDelegateLinker");
3
+
4
+module.exports = () => {
5
+  console.log("Running iOS postlink script\n");
6
+  new AppDelegateLinker().link();
7
+}

+ 8
- 0
autolink/postlink/run.js View File

@@ -0,0 +1,8 @@
1
+// @ts-check
2
+var { infon } = require("./log");
3
+var postLinkAndroid = require('./postLinkAndroid');
4
+var postLinkIOS = require('./postLinkIOS');
5
+
6
+postLinkAndroid();
7
+postLinkIOS();
8
+infon("react-native-navigation linked successfully");

+ 14
- 0
autolink/postlink/stringUtils.js View File

@@ -0,0 +1,14 @@
1
+ // @ts-check
2
+
3
+ /**
4
+   * @param {string} to
5
+   * @param {number} fromIndex
6
+   * @param {string} what
7
+   */
8
+  function insertString(to, fromIndex, what) {
9
+    return to.substring(0, fromIndex) + what + to.substring(fromIndex, to.length);
10
+  }
11
+
12
+  module.exports = {
13
+    insertString
14
+  }

+ 5
- 34
docs/docs/Installing.md View File

@@ -77,8 +77,8 @@
77 77
 
78 78
 Projects generated using newer versions of react-native use CocoaPods by default. In that case it's easier to install react-native-navigation using CocoaPods.
79 79
 
80
-1. Add the following to `Podfile`:
81
-
80
+1. Update your `Podfile`:
81
+If you're upgrading to v5 from a previous RNN version, make sure to remove manual linking of RNN
82 82
 ```diff
83 83
 platform :ios, '9.0'
84 84
 require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
@@ -110,7 +110,7 @@ target 'YourApp' do
110 110
   pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
111 111
   pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
112 112
 
113
-+ pod 'ReactNativeNavigation', :podspec => '../node_modules/react-native-navigation/ReactNativeNavigation.podspec'
113
+- pod 'ReactNativeNavigation', :podspec => '../node_modules/react-native-navigation/ReactNativeNavigation.podspec'
114 114
 
115 115
   use_native_modules!
116 116
 end
@@ -223,36 +223,7 @@ dependencies {
223 223
 }
224 224
 ```
225 225
 
226
-### 5 RNN and React Native version
227
-react-native-navigation supports multiple React Native versions. Target the React Native version required by your project by specifying the RNN build flavor in `android/app/build.gradle`.
228
-
229
-```diff
230
-android {
231
-    ...
232
-    defaultConfig {
233
-        applicationId "com.yourproject"
234
-        minSdkVersion rootProject.ext.minSdkVersion
235
-        targetSdkVersion rootProject.ext.targetSdkVersion
236
-+        missingDimensionStrategy "RNN.reactNativeVersion", "reactNative57" // See note below!
237
-        versionCode 1
238
-        versionName "1.0"
239
-        ...
240
-    }
241
-    ...
242
-}
243
-```
244
-
245
-!>Important note about `missingDimensionStrategy`<Br>
246
->`reactNative51` - RN 0.54.x and below<Br>
247
->`reactNative55` - RN 0.55.x<Br>
248
->`reactNative56` - RN 0.56.x<Br>
249
->`reactNative57` - RN 0.57.0 - 0.57.4<Br>
250
->`reactNative57_5` - RN 0.57.5 - 0.59.9<Br>
251
->`reactNative60` - RN 0.60.0 and above
252
-
253
-Now we need to instruct gradle how to build that flavor. To do so here two solutions:
254
-
255
-#### 5.1 Build app with gradle command
226
+#### 5.0 Build app with gradle command
256 227
 
257 228
 **prefered solution** The RNN flavor you would like to build is specified in `app/build.gradle`. Therefore in order to compile only that flavor, instead of building your entire project using `./gradlew assembleDebug`, you should instruct gradle to build the app module: `./gradlew app:assembleDebug`. The easiest way is to add a package.json command to build and install your debug Android APK .
258 229
 
@@ -265,7 +236,7 @@ Now we need to instruct gradle how to build that flavor. To do so here two solut
265 236
 
266 237
 Now run `npm run android` to build your application
267 238
 
268
-#### 5.2 Ignore other RNN flavors
239
+#### 5.1 Ignore other RNN flavors
269 240
 
270 241
 If you don't want to run `npm run android` and want to keep the default `react-native run-android` command, you need to specify to graddle to ignore the other flavors RNN provides.
271 242
 

+ 64
- 3
lib/android/app/build.gradle View File

@@ -1,4 +1,8 @@
1
+import groovy.json.JsonSlurper
2
+
1 3
 apply plugin: 'com.android.library'
4
+apply plugin: 'kotlin-android'
5
+apply plugin: 'kotlin-android-extensions'
2 6
 
3 7
 def safeExtGet(prop, fallback) {
4 8
     rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
@@ -7,6 +11,8 @@ def safeExtGet(prop, fallback) {
7 11
 def DEFAULT_COMPILE_SDK_VERSION = 28
8 12
 def DEFAULT_MIN_SDK_VERSION = 19
9 13
 def DEFAULT_TARGET_SDK_VERSION = 28
14
+def kotlinVersion = rootProject.ext.get("RNNKotlinVersion")
15
+def kotlinStdlib = safeExtGet('RNNKotlinStdlib', 'kotlin-stdlib-jdk8')
10 16
 
11 17
 android {
12 18
     compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION)
@@ -53,6 +59,7 @@ android {
53 59
         sourceCompatibility JavaVersion.VERSION_1_8
54 60
         targetCompatibility JavaVersion.VERSION_1_8
55 61
     }
62
+
56 63
     flavorDimensions "RNN.reactNativeVersion"
57 64
     productFlavors {
58 65
         reactNative51 {
@@ -84,6 +91,56 @@ android {
84 91
             buildConfigField("int", "REACT_NATVE_VERSION_MINOR", "62")
85 92
         }
86 93
     }
94
+
95
+    def flavor = resolveFlavor()
96
+    variantFilter { variant ->
97
+        def names = variant.flavors*.name
98
+        if (!names.contains(flavor)) {
99
+            setIgnore(true)
100
+        }
101
+    }
102
+}
103
+
104
+String resolveFlavor() {
105
+    List reactNativeVersionComponents = reactNativeVersionComponents(findReactNativePackageJson())
106
+    Integer reactNativeMinorComponent = reactNativeVersionComponents[1].toInteger()
107
+    Integer reactNativePatchComponent = reactNativeVersionComponents[2].toInteger()
108
+
109
+    if (reactNativeMinorComponent >= 62) {
110
+        return "reactNative62"
111
+    } else if (reactNativeMinorComponent >= 60) {
112
+        return "reactNative60"
113
+    } else if (reactNativeMinorComponent >= 57 && reactNativePatchComponent >= 5) {
114
+        return "reactNative57_5"
115
+    } else if (reactNativeMinorComponent >= 57) {
116
+        return "reactNative57"
117
+    } else if (reactNativeMinorComponent >= 56) {
118
+        return "reactNative56"
119
+    } else if (reactNativeMinorComponent >= 55) {
120
+        return "reactNative55"
121
+    } else if (reactNativeMinorComponent >= 51) {
122
+        return "reactNative51"
123
+    }
124
+}
125
+
126
+
127
+Object findReactNativePackageJson() {
128
+    def searchPath = 'node_modules/react-native/package.json'
129
+    def projectDir = project.projectDir.toString() + '/'
130
+    def rnPackageJsonFile = new File(projectDir + searchPath)
131
+    while (!rnPackageJsonFile.exists()) {
132
+        searchPath = '../' + searchPath
133
+        rnPackageJsonFile = new File(projectDir + searchPath)
134
+    }
135
+    return rnPackageJsonFile
136
+}
137
+
138
+List reactNativeVersionComponents(rnPackageJsonFile) {
139
+    def packageSlurper = new JsonSlurper()
140
+    def reactNativePackageJson = packageSlurper.parseText(rnPackageJsonFile.text)
141
+    def reactNativeVersion = reactNativePackageJson.version
142
+
143
+    return reactNativeVersion.tokenize('-')[0].tokenize('.')
87 144
 }
88 145
 
89 146
 allprojects { p ->
@@ -97,12 +154,15 @@ allprojects { p ->
97 154
 }
98 155
 
99 156
 dependencies {
100
-    implementation 'androidx.appcompat:appcompat:1.0.2'
157
+    implementation "androidx.core:core-ktx:1.1.0"
158
+    implementation "org.jetbrains.kotlin:$kotlinStdlib:$kotlinVersion"
159
+
160
+    implementation 'androidx.appcompat:appcompat:1.1.0'
101 161
     implementation 'androidx.annotation:annotation:1.1.0'
102
-    implementation 'com.google.android.material:material:1.1.0-alpha08'
162
+    implementation 'com.google.android.material:material:1.2.0-alpha03'
103 163
 
104 164
     implementation 'com.github.wix-playground:ahbottomnavigation:3.1.2'
105
-    implementation 'com.github.wix-playground:reflow-animator:1.0.4'
165
+    implementation 'com.github.wix-playground:reflow-animator:1.0.6'
106 166
     implementation 'com.github.clans:fab:1.6.4'
107 167
 
108 168
     //noinspection GradleDynamicVersion
@@ -114,4 +174,5 @@ dependencies {
114 174
     testImplementation 'org.assertj:assertj-core:3.8.0'
115 175
     testImplementation 'com.squareup.assertj:assertj-android:1.1.1'
116 176
     testImplementation 'org.mockito:mockito-core:2.28.2'
177
+    testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
117 178
 }

+ 5
- 4
lib/android/app/src/main/java/com/reactnativenavigation/NavigationActivity.java View File

@@ -5,9 +5,6 @@ import android.content.Intent;
5 5
 import android.graphics.Color;
6 6
 import android.os.Build;
7 7
 import android.os.Bundle;
8
-import androidx.annotation.NonNull;
9
-import androidx.annotation.Nullable;
10
-import androidx.appcompat.app.AppCompatActivity;
11 8
 import android.view.KeyEvent;
12 9
 import android.view.View;
13 10
 
@@ -15,13 +12,17 @@ import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
15 12
 import com.facebook.react.modules.core.PermissionAwareActivity;
16 13
 import com.facebook.react.modules.core.PermissionListener;
17 14
 import com.reactnativenavigation.presentation.OverlayManager;
15
+import com.reactnativenavigation.presentation.RootPresenter;
18 16
 import com.reactnativenavigation.react.JsDevReloadHandler;
19 17
 import com.reactnativenavigation.react.ReactGateway;
20 18
 import com.reactnativenavigation.utils.CommandListenerAdapter;
21 19
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
22 20
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
23 21
 import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
24
-import com.reactnativenavigation.presentation.RootPresenter;
22
+
23
+import androidx.annotation.NonNull;
24
+import androidx.annotation.Nullable;
25
+import androidx.appcompat.app.AppCompatActivity;
25 26
 
26 27
 public class NavigationActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler, PermissionAwareActivity, JsDevReloadHandler.ReloadListener {
27 28
     @Nullable

+ 4
- 24
lib/android/app/src/main/java/com/reactnativenavigation/NavigationApplication.java View File

@@ -4,17 +4,14 @@ import android.app.Application;
4 4
 
5 5
 import com.facebook.react.ReactApplication;
6 6
 import com.facebook.react.ReactNativeHost;
7
-import com.facebook.react.ReactPackage;
8
-import com.reactnativenavigation.react.NavigationReactNativeHost;
7
+import com.facebook.soloader.SoLoader;
9 8
 import com.reactnativenavigation.react.ReactGateway;
10 9
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentCreator;
11 10
 
12 11
 import java.util.HashMap;
13
-import java.util.List;
14 12
 import java.util.Map;
15 13
 
16 14
 import androidx.annotation.NonNull;
17
-import androidx.annotation.Nullable;
18 15
 
19 16
 public abstract class NavigationApplication extends Application implements ReactApplication {
20 17
 
@@ -26,6 +23,7 @@ public abstract class NavigationApplication extends Application implements React
26 23
 	public void onCreate() {
27 24
 		super.onCreate();
28 25
         instance = this;
26
+        SoLoader.init(this, false);
29 27
         reactGateway = createReactGateway();
30 28
 	}
31 29
 
@@ -39,22 +37,13 @@ public abstract class NavigationApplication extends Application implements React
39 37
      * @return a singleton {@link ReactGateway}
40 38
      */
41 39
 	protected ReactGateway createReactGateway() {
42
-	    return new ReactGateway(this, isDebug(), this::createReactNativeHost);
40
+	    return new ReactGateway(getReactNativeHost());
43 41
     }
44
-
45
-    protected ReactNativeHost createReactNativeHost() {
46
-        return new NavigationReactNativeHost(this);
47
-    }
48
-
42
+    
49 43
 	public ReactGateway getReactGateway() {
50 44
 		return reactGateway;
51 45
 	}
52 46
 
53
-	@Override
54
-	public ReactNativeHost getReactNativeHost() {
55
-		return getReactGateway().getReactNativeHost();
56
-	}
57
-
58 47
     /**
59 48
      * Generally no need to override this; override for custom permission handling.
60 49
      */
@@ -62,15 +51,6 @@ public abstract class NavigationApplication extends Application implements React
62 51
 
63 52
     }
64 53
 
65
-	public abstract boolean isDebug();
66
-
67
-    /**
68
-     * Create a list of additional {@link ReactPackage}s to include. This method will only be called by
69
-     * the default implementation of {@link #createReactGateway()}
70
-     */
71
-	@Nullable
72
-	public abstract List<ReactPackage> createAdditionalReactPackages();
73
-
74 54
     /**
75 55
      * Register a native View which can be displayed using the given {@code name}
76 56
      * @param name Unique name used to register the native view

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

@@ -4,22 +4,21 @@ import android.animation.Animator;
4 4
 import android.animation.AnimatorListenerAdapter;
5 5
 import android.animation.AnimatorSet;
6 6
 import android.content.Context;
7
-import androidx.annotation.RestrictTo;
8 7
 import android.view.View;
9
-import android.view.ViewGroup;
10 8
 
11 9
 import com.reactnativenavigation.parse.AnimationOptions;
10
+import com.reactnativenavigation.parse.FadeAnimation;
12 11
 import com.reactnativenavigation.parse.NestedAnimationsOptions;
13
-import com.reactnativenavigation.parse.Transitions;
14
-import com.reactnativenavigation.views.element.Element;
12
+import com.reactnativenavigation.parse.Options;
13
+import com.reactnativenavigation.viewcontrollers.ViewController;
15 14
 import com.reactnativenavigation.views.element.ElementTransitionManager;
16 15
 
17
-import java.util.Collection;
18
-import java.util.Collections;
16
+import java.util.ArrayList;
19 17
 import java.util.HashMap;
20
-import java.util.List;
21 18
 import java.util.Map;
22 19
 
20
+import androidx.annotation.RestrictTo;
21
+
23 22
 import static com.reactnativenavigation.utils.CollectionUtils.*;
24 23
 
25 24
 @SuppressWarnings("ResourceType")
@@ -33,41 +32,48 @@ public class NavigationAnimator extends BaseAnimator {
33 32
         this.transitionManager = transitionManager;
34 33
     }
35 34
 
36
-    public void push(ViewGroup view, NestedAnimationsOptions animation, Runnable onAnimationEnd) {
37
-        push(view, animation, new Transitions(), Collections.EMPTY_LIST, Collections.EMPTY_LIST, onAnimationEnd);
38
-    }
39
-
40
-    public void push(ViewGroup view, NestedAnimationsOptions animation, Transitions transitions, List<Element> fromElements, List<Element> toElements, Runnable onAnimationEnd) {
41
-        view.setAlpha(0);
42
-        AnimatorSet push = animation.content.getAnimation(view, getDefaultPushAnimation(view));
43
-        AnimatorSet set = new AnimatorSet();
44
-        Collection<Animator> elementTransitions = transitionManager.createTransitions(transitions, fromElements, toElements);
45
-        set.playTogether(merge(push.getChildAnimations(), elementTransitions));
46
-        set.addListener(new AnimatorListenerAdapter() {
47
-            private boolean isCancelled;
48
-
49
-            @Override
50
-            public void onAnimationStart(Animator animation) {
51
-                view.setAlpha(1);
52
-            }
53
-
54
-            @Override
55
-            public void onAnimationCancel(Animator animation) {
56
-                isCancelled = true;
57
-                runningPushAnimations.remove(view);
58
-                onAnimationEnd.run();
59
-            }
60
-
61
-            @Override
62
-            public void onAnimationEnd(Animator animation) {
63
-                if (!isCancelled) {
64
-                    runningPushAnimations.remove(view);
65
-                    onAnimationEnd.run();
35
+    public void push(ViewController appearing, ViewController disappearing, Options options, Runnable onAnimationEnd) {
36
+        appearing.getView().setAlpha(0);
37
+        transitionManager.createTransitions(
38
+                options.animations.push,
39
+                disappearing,
40
+                appearing,
41
+                transitionSet -> {
42
+                    AnimatorSet set = new AnimatorSet();
43
+                    runningPushAnimations.put(appearing.getView(), set);
44
+                    set.addListener(new AnimatorListenerAdapter() {
45
+                        private boolean isCancelled;
46
+
47
+                        @Override
48
+                        public void onAnimationCancel(Animator animation) {
49
+                            isCancelled = true;
50
+                            runningPushAnimations.remove(appearing.getView());
51
+                            onAnimationEnd.run();
52
+                        }
53
+
54
+                        @Override
55
+                        public void onAnimationEnd(Animator animation) {
56
+                            if (!isCancelled) {
57
+                                runningPushAnimations.remove(appearing.getView());
58
+                                onAnimationEnd.run();
59
+                            }
60
+                        }
61
+                    });
62
+
63
+
64
+                    if (transitionSet.isEmpty()) {
65
+                        set.playTogether(options.animations.push.content.getAnimation(appearing.getView(), getDefaultPushAnimation(appearing.getView())));
66
+                    } else {
67
+                        AnimationOptions fade = options.animations.push.content.isFadeAnimation() ? options.animations.push.content : new FadeAnimation().content;
68
+                        AnimatorSet transitions = transitionManager.createAnimators(fade, transitionSet);
69
+                        ArrayList<Animator.AnimatorListener> listeners = transitions.getListeners();
70
+                        set.playTogether(fade.getAnimation(appearing.getView()), transitions);
71
+                        forEach(listeners, set::addListener);
72
+                        transitions.removeAllListeners();
73
+                    }
74
+                    set.start();
66 75
                 }
67
-            }
68
-        });
69
-        runningPushAnimations.put(view, set);
70
-        set.start();
76
+        );
71 77
     }
72 78
 
73 79
     public void pop(View view, NestedAnimationsOptions pop, Runnable onAnimationEnd) {

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

@@ -1,119 +0,0 @@
1
-package com.reactnativenavigation.parse;
2
-
3
-
4
-import android.animation.Animator;
5
-import android.animation.AnimatorSet;
6
-import android.util.Property;
7
-import android.view.View;
8
-
9
-import com.reactnativenavigation.parse.params.Bool;
10
-import com.reactnativenavigation.parse.params.NullBool;
11
-import com.reactnativenavigation.parse.params.NullText;
12
-import com.reactnativenavigation.parse.params.Text;
13
-import com.reactnativenavigation.parse.parsers.BoolParser;
14
-import com.reactnativenavigation.parse.parsers.TextParser;
15
-
16
-import org.json.JSONObject;
17
-
18
-import java.util.ArrayList;
19
-import java.util.HashSet;
20
-import java.util.Iterator;
21
-import java.util.List;
22
-
23
-import static com.reactnativenavigation.utils.CollectionUtils.*;
24
-
25
-public class AnimationOptions {
26
-    public static AnimationOptions parse(JSONObject json) {
27
-        AnimationOptions options = new AnimationOptions();
28
-        if (json == null) return options;
29
-
30
-        for (Iterator<String> it = json.keys(); it.hasNext(); ) {
31
-            String key = it.next();
32
-            switch (key) {
33
-                case "id":
34
-                    options.id = TextParser.parse(json, key);
35
-                    break;
36
-                case "enable":
37
-                case "enabled":
38
-                    options.enabled = BoolParser.parse(json, key);
39
-                    break;
40
-                case "waitForRender":
41
-                    options.waitForRender = BoolParser.parse(json, key);
42
-                    break;
43
-                default:
44
-                    options.valueOptions.add(ValueAnimationOptions.parse(json.optJSONObject(key), getAnimProp(key)));
45
-            }
46
-        }
47
-
48
-        return options;
49
-    }
50
-
51
-    public Text id = new NullText();
52
-    public Bool enabled = new NullBool();
53
-    public Bool waitForRender = new NullBool();
54
-    private HashSet<ValueAnimationOptions> valueOptions = new HashSet<>();
55
-
56
-    void mergeWith(AnimationOptions other) {
57
-        if (other.id.hasValue()) id = other.id;
58
-        if (other.enabled.hasValue()) enabled = other.enabled;
59
-        if (other.waitForRender.hasValue()) waitForRender = other.waitForRender;
60
-        if (!other.valueOptions.isEmpty()) valueOptions = other.valueOptions;
61
-    }
62
-
63
-    void mergeWithDefault(AnimationOptions defaultOptions) {
64
-        if (!id.hasValue()) id = defaultOptions.id;
65
-        if (!enabled.hasValue()) enabled = defaultOptions.enabled;
66
-        if (!waitForRender.hasValue()) waitForRender = defaultOptions.waitForRender;
67
-        if (valueOptions.isEmpty()) valueOptions = defaultOptions.valueOptions;
68
-    }
69
-
70
-    public boolean hasValue() {
71
-        return id.hasValue() || enabled.hasValue() || waitForRender.hasValue();
72
-    }
73
-
74
-    public AnimatorSet getAnimation(View view) {
75
-        return getAnimation(view, null);
76
-    }
77
-
78
-    public AnimatorSet getAnimation(View view, AnimatorSet defaultAnimation) {
79
-        if (!hasAnimation()) return defaultAnimation;
80
-        AnimatorSet animationSet = new AnimatorSet();
81
-        List<Animator> animators = new ArrayList<>();
82
-        forEach(valueOptions, options -> animators.add(options.getAnimation(view)));
83
-        animationSet.playTogether(animators);
84
-        return animationSet;
85
-    }
86
-
87
-    private static Property<View, Float> getAnimProp(String key) {
88
-        switch (key) {
89
-            case "y":
90
-                return View.TRANSLATION_Y;
91
-            case "x":
92
-                return View.TRANSLATION_X;
93
-            case "alpha":
94
-                return View.ALPHA;
95
-            case "scaleY":
96
-                return View.SCALE_Y;
97
-            case "scaleX":
98
-                return View.SCALE_X;
99
-            case "rotationX":
100
-                return View.ROTATION_X;
101
-            case "rotationY":
102
-                return View.ROTATION_Y;
103
-            case "rotation":
104
-                return View.ROTATION;
105
-        }
106
-        throw new IllegalArgumentException("This animation is not supported: " + key);
107
-    }
108
-
109
-    public boolean hasAnimation() {
110
-        return !valueOptions.isEmpty();
111
-    }
112
-
113
-    public void setValueDy(Property<View, Float> animation, float fromDelta, float toDelta) {
114
-        first(valueOptions, o -> o.equals(animation), param -> {
115
-            param.setFromDelta(fromDelta);
116
-            param.setToDelta(toDelta);
117
-        });
118
-    }
119
-}

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

@@ -0,0 +1,111 @@
1
+package com.reactnativenavigation.parse
2
+
3
+import android.animation.Animator
4
+import android.animation.AnimatorSet
5
+import android.util.Property
6
+import android.util.TypedValue.COMPLEX_UNIT_DIP
7
+import android.util.TypedValue.COMPLEX_UNIT_FRACTION
8
+import android.view.View
9
+import android.view.View.*
10
+import com.reactnativenavigation.parse.params.Bool
11
+import com.reactnativenavigation.parse.params.NullBool
12
+import com.reactnativenavigation.parse.params.NullText
13
+import com.reactnativenavigation.parse.params.Text
14
+import com.reactnativenavigation.parse.parsers.BoolParser
15
+import com.reactnativenavigation.parse.parsers.TextParser
16
+import com.reactnativenavigation.utils.CollectionUtils
17
+import org.json.JSONObject
18
+import java.util.*
19
+import kotlin.math.max
20
+
21
+open class AnimationOptions(json: JSONObject?) {
22
+    constructor() : this(null)
23
+
24
+    private fun parse(json: JSONObject?) {
25
+        json?.let {
26
+            val iter = json.keys()
27
+            while (iter.hasNext()) {
28
+                when (val key = iter.next()) {
29
+                    "id" -> id = TextParser.parse(json, key)
30
+                    "enable", "enabled" -> enabled = BoolParser.parse(json, key)
31
+                    "waitForRender" -> waitForRender = BoolParser.parse(json, key)
32
+                    else -> valueOptions.add(ValueAnimationOptions.parse(json.optJSONObject(key), getAnimProp(key)))
33
+                }
34
+            }
35
+        }
36
+    }
37
+
38
+    @JvmField
39
+    var id: Text = NullText()
40
+    @JvmField
41
+    var enabled: Bool = NullBool()
42
+    @JvmField
43
+    var waitForRender: Bool = NullBool()
44
+    private var valueOptions = HashSet<ValueAnimationOptions>()
45
+    fun mergeWith(other: AnimationOptions) {
46
+        if (other.id.hasValue()) id = other.id
47
+        if (other.enabled.hasValue()) enabled = other.enabled
48
+        if (other.waitForRender.hasValue()) waitForRender = other.waitForRender
49
+        if (other.valueOptions.isNotEmpty()) valueOptions = other.valueOptions
50
+    }
51
+
52
+    fun mergeWithDefault(defaultOptions: AnimationOptions) {
53
+        if (!id.hasValue()) id = defaultOptions.id
54
+        if (!enabled.hasValue()) enabled = defaultOptions.enabled
55
+        if (!waitForRender.hasValue()) waitForRender = defaultOptions.waitForRender
56
+        if (valueOptions.isEmpty()) valueOptions = defaultOptions.valueOptions
57
+    }
58
+
59
+    fun hasValue(): Boolean {
60
+        return id.hasValue() || enabled.hasValue() || waitForRender.hasValue()
61
+    }
62
+
63
+    fun getAnimation(view: View): AnimatorSet {
64
+        return getAnimation(view, AnimatorSet())
65
+    }
66
+
67
+    fun getAnimation(view: View, defaultAnimation: AnimatorSet): AnimatorSet {
68
+        if (!hasAnimation()) return defaultAnimation
69
+        val animationSet = AnimatorSet()
70
+        val animators: MutableList<Animator> = ArrayList()
71
+        CollectionUtils.forEach(valueOptions) { options: ValueAnimationOptions -> animators.add(options.getAnimation(view)) }
72
+        animationSet.playTogether(animators)
73
+        return animationSet
74
+    }
75
+
76
+    val duration: Int
77
+        get() = CollectionUtils.reduce(valueOptions, 0, { item: ValueAnimationOptions, currentValue: Int -> max(item.duration[currentValue], currentValue) })
78
+
79
+    open fun hasAnimation(): Boolean = valueOptions.isNotEmpty()
80
+
81
+    fun isFadeAnimation(): Boolean = valueOptions.size == 1 && valueOptions.find(ValueAnimationOptions::isAlpha) != null
82
+
83
+    fun setValueDy(animation: Property<View?, Float?>?, fromDelta: Float, toDelta: Float) {
84
+        CollectionUtils.first(valueOptions, { o: ValueAnimationOptions -> o.equals(animation) }) { param: ValueAnimationOptions ->
85
+            param.setFromDelta(fromDelta)
86
+            param.setToDelta(toDelta)
87
+        }
88
+    }
89
+
90
+    companion object {
91
+        private fun getAnimProp(key: String): Triple<Property<View, Float>, Int, (View) -> Float> {
92
+            when (key) {
93
+                "x" -> return Triple(X, COMPLEX_UNIT_DIP, View::getX)
94
+                "y" -> return Triple(Y, COMPLEX_UNIT_DIP, View::getY)
95
+                "translationX" -> return Triple(TRANSLATION_X, COMPLEX_UNIT_DIP, View::getTranslationX)
96
+                "translationY" -> return Triple(TRANSLATION_Y, COMPLEX_UNIT_DIP, View::getTranslationY)
97
+                "alpha" -> return Triple(ALPHA, COMPLEX_UNIT_FRACTION, View::getAlpha)
98
+                "scaleX" -> return Triple(SCALE_X, COMPLEX_UNIT_FRACTION, View::getScaleX)
99
+                "scaleY" -> return Triple(SCALE_Y, COMPLEX_UNIT_FRACTION, View::getScaleY)
100
+                "rotationX" -> return Triple(ROTATION_X, COMPLEX_UNIT_FRACTION, View::getRotationX)
101
+                "rotationY" -> return Triple(ROTATION_Y, COMPLEX_UNIT_FRACTION, View::getRotationY)
102
+                "rotation" -> return Triple(ROTATION, COMPLEX_UNIT_FRACTION, View::getRotation)
103
+            }
104
+            throw IllegalArgumentException("This animation is not supported: $key")
105
+        }
106
+    }
107
+
108
+    init {
109
+        json?.let { parse(it) }
110
+    }
111
+}

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

@@ -12,9 +12,9 @@ public class AnimationsOptions {
12 12
         options.push = NestedAnimationsOptions.parse(json.optJSONObject("push"));
13 13
         options.pop = NestedAnimationsOptions.parse(json.optJSONObject("pop"));
14 14
         options.setStackRoot = NestedAnimationsOptions.parse(json.optJSONObject("setStackRoot"));
15
-        options.setRoot = AnimationOptions.parse(json.optJSONObject("setRoot"));
16
-        options.showModal = AnimationOptions.parse(json.optJSONObject("showModal"));
17
-        options.dismissModal = AnimationOptions.parse(json.optJSONObject("dismissModal"));
15
+        options.setRoot = new AnimationOptions(json.optJSONObject("setRoot"));
16
+        options.showModal = new AnimationOptions(json.optJSONObject("showModal"));
17
+        options.dismissModal = new AnimationOptions(json.optJSONObject("dismissModal"));
18 18
 
19 19
         return options;
20 20
     }

+ 13
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/ElementTransitionOptions.kt View File

@@ -0,0 +1,13 @@
1
+package com.reactnativenavigation.parse
2
+
3
+import android.animation.AnimatorSet
4
+import android.view.View
5
+import org.json.JSONObject
6
+
7
+class ElementTransitionOptions(json: JSONObject) {
8
+    private val animation: AnimationOptions = AnimationOptions(json)
9
+    val id: String
10
+        get() = animation.id.get()
11
+
12
+    fun getAnimation(view: View): AnimatorSet = animation.getAnimation(view)
13
+}

+ 34
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/ElementTransitions.kt View File

@@ -0,0 +1,34 @@
1
+package com.reactnativenavigation.parse
2
+
3
+import org.json.JSONException
4
+import org.json.JSONObject
5
+
6
+class ElementTransitions {
7
+    var transitions = arrayListOf<ElementTransitionOptions>()
8
+    val hasValue: Boolean
9
+        get() = transitions.isNotEmpty()
10
+
11
+    companion object {
12
+        fun parse(json: JSONObject): ElementTransitions {
13
+            val result = ElementTransitions()
14
+            try {
15
+                val elementTransitions = json.getJSONArray("elementTransitions")
16
+                if (elementTransitions.length() == 0) return result
17
+                for (i in 0..elementTransitions.length()) {
18
+                    result.transitions.add(ElementTransitionOptions(elementTransitions.getJSONObject(i)))
19
+                }
20
+            } catch (e: JSONException) {
21
+
22
+            }
23
+            return result
24
+        }
25
+    }
26
+
27
+    fun mergeWith(other: ElementTransitions) {
28
+        if (other.hasValue) transitions = other.transitions
29
+    }
30
+
31
+    fun mergeWithDefault(defaultOptions: ElementTransitions) {
32
+        if (!hasValue) transitions = defaultOptions.transitions
33
+    }
34
+}

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

@@ -0,0 +1,24 @@
1
+package com.reactnativenavigation.parse;
2
+
3
+import org.json.JSONException;
4
+import org.json.JSONObject;
5
+
6
+public class FadeAnimation extends NestedAnimationsOptions {
7
+    public FadeAnimation() {
8
+        try {
9
+            JSONObject alpha = new JSONObject();
10
+            alpha.put("from", 0);
11
+            alpha.put("to", 1);
12
+            alpha.put("duration", 300);
13
+
14
+            JSONObject content = new JSONObject();
15
+            content.put("alpha", alpha);
16
+
17
+            JSONObject animation = new JSONObject();
18
+            animation.put("content", content);
19
+            mergeWith(parse(animation));
20
+        } catch (JSONException e) {
21
+            e.printStackTrace();
22
+        }
23
+    }
24
+}

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

@@ -12,11 +12,13 @@ public class NestedAnimationsOptions {
12 12
         NestedAnimationsOptions options = new NestedAnimationsOptions();
13 13
         if (json == null) return options;
14 14
 
15
-        options.content = AnimationOptions.parse(json.optJSONObject("content"));
16
-        options.bottomTabs = AnimationOptions.parse(json.optJSONObject("bottomTabs"));
17
-        options.topBar = AnimationOptions.parse(json.optJSONObject("topBar"));
15
+        options.content = new AnimationOptions(json.optJSONObject("content"));
16
+        options.bottomTabs = new AnimationOptions(json.optJSONObject("bottomTabs"));
17
+        options.topBar = new AnimationOptions(json.optJSONObject("topBar"));
18 18
         options.enabled = BoolParser.parseFirst(json, "enabled", "enable");
19 19
         options.waitForRender = BoolParser.parse(json, "waitForRender");
20
+        options.sharedElements = SharedElements.parse(json);
21
+        options.elementTransitions = ElementTransitions.Companion.parse(json);
20 22
 
21 23
         return options;
22 24
     }
@@ -26,11 +28,15 @@ public class NestedAnimationsOptions {
26 28
     public AnimationOptions content = new AnimationOptions();
27 29
     public AnimationOptions bottomTabs = new AnimationOptions();
28 30
     public AnimationOptions topBar = new AnimationOptions();
31
+    public SharedElements sharedElements = new SharedElements();
32
+    public ElementTransitions elementTransitions = new ElementTransitions();
29 33
 
30 34
     void mergeWith(NestedAnimationsOptions other) {
31 35
         topBar.mergeWith(other.topBar);
32 36
         content.mergeWith(other.content);
33 37
         bottomTabs.mergeWith(other.bottomTabs);
38
+        sharedElements.mergeWith(other.sharedElements);
39
+        elementTransitions.mergeWith(other.elementTransitions);
34 40
         if (other.enabled.hasValue()) enabled = other.enabled;
35 41
         if (other.waitForRender.hasValue()) waitForRender = other.waitForRender;
36 42
     }
@@ -39,6 +45,8 @@ public class NestedAnimationsOptions {
39 45
         content.mergeWithDefault(defaultOptions.content);
40 46
         bottomTabs.mergeWithDefault(defaultOptions.bottomTabs);
41 47
         topBar.mergeWithDefault(defaultOptions.topBar);
48
+        sharedElements.mergeWithDefault(defaultOptions.sharedElements);
49
+        elementTransitions.mergeWithDefault(defaultOptions.elementTransitions);
42 50
         if (!enabled.hasValue()) enabled = defaultOptions.enabled;
43 51
         if (!waitForRender.hasValue()) waitForRender = defaultOptions.waitForRender;
44 52
     }

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

@@ -30,7 +30,6 @@ public class Options {
30 30
         result.navigationBar = NavigationBarOptions.parse(json.optJSONObject("navigationBar"));
31 31
         result.statusBar = StatusBarOptions.parse(json.optJSONObject("statusBar"));
32 32
         result.layout = LayoutOptions.parse(json.optJSONObject("layout"));
33
-        result.transitions = Transitions.parse(json.optJSONObject("customTransition"));
34 33
 
35 34
         return result;
36 35
     }
@@ -48,7 +47,6 @@ public class Options {
48 47
     @NonNull public NavigationBarOptions navigationBar = new NavigationBarOptions();
49 48
     @NonNull public StatusBarOptions statusBar = new StatusBarOptions();
50 49
     @NonNull public LayoutOptions layout = new LayoutOptions();
51
-    @NonNull public Transitions transitions = new Transitions();
52 50
 
53 51
     void setTopTabIndex(int i) {
54 52
         topTabOptions.tabIndex = i;
@@ -70,7 +68,6 @@ public class Options {
70 68
         result.navigationBar.mergeWith(navigationBar);
71 69
         result.statusBar.mergeWith(statusBar);
72 70
         result.layout.mergeWith(layout);
73
-        result.transitions.mergeWith(transitions);
74 71
         return result;
75 72
     }
76 73
 
@@ -89,7 +86,6 @@ public class Options {
89 86
         result.navigationBar.mergeWith(other.navigationBar);
90 87
         result.statusBar.mergeWith(other.statusBar);
91 88
         result.layout.mergeWith(other.layout);
92
-        result.transitions.mergeWith(transitions);
93 89
         return result;
94 90
     }
95 91
 
@@ -106,7 +102,6 @@ public class Options {
106 102
         navigationBar.mergeWithDefault(defaultOptions.navigationBar);
107 103
         statusBar.mergeWithDefault(defaultOptions.statusBar);
108 104
         layout.mergeWithDefault(defaultOptions.layout);
109
-        transitions.mergeWithDefault(defaultOptions.transitions);
110 105
         return this;
111 106
     }
112 107
 

lib/android/app/src/main/java/com/reactnativenavigation/parse/Transition.java → lib/android/app/src/main/java/com/reactnativenavigation/parse/SharedElementTransitionOptions.java View File

@@ -1,50 +1,45 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3
-import com.reactnativenavigation.parse.params.Fraction;
4 3
 import com.reactnativenavigation.parse.params.NullNumber;
5 4
 import com.reactnativenavigation.parse.params.NullText;
6 5
 import com.reactnativenavigation.parse.params.Number;
7 6
 import com.reactnativenavigation.parse.params.Text;
8
-import com.reactnativenavigation.parse.parsers.FractionParser;
7
+import com.reactnativenavigation.parse.parsers.NumberParser;
9 8
 import com.reactnativenavigation.parse.parsers.TextParser;
10 9
 
11 10
 import org.json.JSONObject;
12 11
 
13
-public class Transition {
12
+import androidx.annotation.Nullable;
13
+
14
+public class SharedElementTransitionOptions {
14 15
     public Text fromId = new NullText();
15 16
     public Text toId = new NullText();
16
-    public Number startDelay = new NullNumber();
17 17
     public Number duration = new NullNumber();
18 18
 
19
-    public static Transition parse(JSONObject json) {
20
-        Transition transition = new Transition();
19
+    public static SharedElementTransitionOptions parse(@Nullable JSONObject json) {
20
+        SharedElementTransitionOptions transition = new SharedElementTransitionOptions();
21 21
         if (json == null) return transition;
22 22
 
23 23
         transition.fromId = TextParser.parse(json, "fromId");
24 24
         transition.toId = TextParser.parse(json, "toId");
25
-        Fraction startDelay = FractionParser.parse(json, "startDelay");
26
-        if (startDelay.hasValue()) {
27
-            transition.startDelay = new Number((int) (startDelay.get() * 1000));
28
-        }
29
-        Fraction duration = FractionParser.parse(json, "duration");
30
-        if (duration.hasValue()) {
31
-            transition.duration = new Number((int) (duration.get() * 1000));
32
-        }
25
+        transition.duration = NumberParser.parse(json, "duration");
33 26
 
34 27
         return transition;
35 28
     }
36 29
 
37
-    void mergeWith(Transition other) {
30
+    void mergeWith(SharedElementTransitionOptions other) {
38 31
         if (other.fromId.hasValue()) fromId = other.fromId;
39 32
         if (other.toId.hasValue()) toId = other.toId;
40
-        if (other.startDelay.hasValue()) startDelay = other.startDelay;
41 33
         if (other.duration.hasValue()) duration = other.duration;
42 34
     }
43 35
 
44
-    void mergeWithDefault(Transition defaultOptions) {
36
+    void mergeWithDefault(SharedElementTransitionOptions defaultOptions) {
45 37
         if (!fromId.hasValue()) fromId = defaultOptions.fromId;
46 38
         if (!toId.hasValue()) toId = defaultOptions.toId;
47
-        if (!startDelay.hasValue()) startDelay = defaultOptions.startDelay;
48 39
         if (!duration.hasValue()) duration = defaultOptions.duration;
49 40
     }
41
+
42
+    public long getDuration() {
43
+        return duration.get(0).longValue();
44
+    }
50 45
 }

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

@@ -0,0 +1,57 @@
1
+package com.reactnativenavigation.parse;
2
+
3
+import org.json.JSONArray;
4
+import org.json.JSONException;
5
+import org.json.JSONObject;
6
+
7
+import java.util.ArrayList;
8
+import java.util.List;
9
+
10
+import androidx.annotation.RestrictTo;
11
+
12
+public class SharedElements {
13
+    private List<SharedElementTransitionOptions> transitions = new ArrayList<>();
14
+
15
+    public List<SharedElementTransitionOptions> get() {
16
+        return transitions;
17
+    }
18
+
19
+    public SharedElements() {
20
+
21
+    }
22
+
23
+    @RestrictTo(RestrictTo.Scope.TESTS)
24
+    public SharedElements(List<SharedElementTransitionOptions> transitions) {
25
+        this.transitions = transitions;
26
+    }
27
+
28
+    public static SharedElements parse(JSONObject json) {
29
+        SharedElements result = new SharedElements();
30
+        try {
31
+            JSONArray sharedElements = json.getJSONArray("sharedElementTransitions");
32
+            if (sharedElements == null || sharedElements.length() == 0) return result;
33
+            for (int i = 0; i < sharedElements.length(); i++) {
34
+                result.add(SharedElementTransitionOptions.parse(sharedElements.getJSONObject(i)));
35
+            }
36
+            return result;
37
+        } catch (JSONException e) {
38
+            return result;
39
+        }
40
+    }
41
+
42
+    public boolean hasValue() {
43
+        return !transitions.isEmpty();
44
+    }
45
+
46
+    void mergeWith(final SharedElements other) {
47
+        if (other.hasValue()) transitions = other.transitions;
48
+    }
49
+
50
+    void mergeWithDefault(SharedElements defaultOptions) {
51
+        if (!hasValue()) transitions = defaultOptions.transitions;
52
+    }
53
+
54
+    private void add(SharedElementTransitionOptions transition) {
55
+        transitions.add(transition);
56
+    }
57
+}

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

@@ -1,49 +0,0 @@
1
-package com.reactnativenavigation.parse;
2
-
3
-import androidx.annotation.RestrictTo;
4
-
5
-import org.json.JSONArray;
6
-import org.json.JSONObject;
7
-
8
-import java.util.ArrayList;
9
-import java.util.List;
10
-
11
-public class Transitions {
12
-    private List<Transition> transitions = new ArrayList<>();
13
-
14
-    public List<Transition> get() {
15
-        return transitions;
16
-    }
17
-
18
-    public Transitions() {
19
-
20
-    }
21
-
22
-    @RestrictTo(RestrictTo.Scope.TESTS)
23
-    public Transitions(List<Transition> transitions) {
24
-        this.transitions = transitions;
25
-    }
26
-
27
-    public static Transitions parse(JSONObject json) {
28
-        Transitions result = new Transitions();
29
-        if (json != null && json.has("animations")) {
30
-            JSONArray animations = json.optJSONArray("animations");
31
-            for (int i = 0; i < animations.length(); i++) {
32
-                result.transitions.add(Transition.parse(animations.optJSONObject(i)));
33
-            }
34
-        }
35
-        return result;
36
-    }
37
-
38
-    public boolean hasValue() {
39
-        return !transitions.isEmpty();
40
-    }
41
-
42
-    void mergeWith(final Transitions other) {
43
-        if (other.hasValue()) transitions = other.transitions;
44
-    }
45
-
46
-    void mergeWithDefault(Transitions defaultOptions) {
47
-        if (!hasValue()) transitions = defaultOptions.transitions;
48
-    }
49
-}

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

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

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

@@ -0,0 +1,88 @@
1
+package com.reactnativenavigation.parse
2
+
3
+import android.animation.Animator
4
+import android.animation.ObjectAnimator
5
+import android.util.Property
6
+import android.util.TypedValue
7
+import android.view.View
8
+import com.reactnativenavigation.parse.params.*
9
+import com.reactnativenavigation.parse.params.Number
10
+import com.reactnativenavigation.parse.parsers.FloatParser
11
+import com.reactnativenavigation.parse.parsers.InterpolationParser
12
+import com.reactnativenavigation.parse.parsers.NumberParser
13
+import com.reactnativenavigation.utils.UiUtils.dpToPx
14
+import org.json.JSONObject
15
+
16
+class ValueAnimationOptions {
17
+    private var animProp: Property<View, Float>? = null
18
+    private var animPropType: Int? = null
19
+    private var animationValueAccessor: ((View) -> Float)? = null
20
+    private var from: FloatParam = NullFloatParam()
21
+    private var fromDelta = FloatParam(0f)
22
+    private var to: FloatParam = NullFloatParam()
23
+    private var toDelta = FloatParam(0f)
24
+    var duration: Number = NullNumber()
25
+    private var startDelay: Number = NullNumber()
26
+    private var interpolation = Interpolation.NO_VALUE
27
+
28
+    fun setFromDelta(fromDelta: Float) {
29
+        this.fromDelta = FloatParam(fromDelta)
30
+    }
31
+
32
+    fun setToDelta(toDelta: Float) {
33
+        this.toDelta = FloatParam(toDelta)
34
+    }
35
+
36
+    fun getAnimation(view: View): Animator {
37
+        require(!(!from.hasValue() && !to.hasValue())) { "Params 'from' and 'to' are mandatory" }
38
+
39
+        var from = fromDelta.get()
40
+        var to = toDelta.get()
41
+        if (animPropType == TypedValue.COMPLEX_UNIT_DIP) {
42
+            from += dpToPx(view.context, this.from[animationValueAccessor!!(view)])
43
+            to += dpToPx(view.context, this.to[animationValueAccessor!!(view)])
44
+        } else {
45
+            from += this.from[animationValueAccessor!!(view)]
46
+            to += this.to[animationValueAccessor!!(view)]
47
+        }
48
+        val animator = ObjectAnimator.ofFloat(view,
49
+                animProp,
50
+                from,
51
+                to
52
+        )
53
+        animator.interpolator = interpolation.interpolator
54
+        if (duration.hasValue()) animator.duration = duration.get().toLong()
55
+        if (startDelay.hasValue()) animator.startDelay = startDelay.get().toLong()
56
+        return animator
57
+    }
58
+
59
+    override fun equals(o: Any?): Boolean {
60
+        if (this === o) return true
61
+        return if (o == null || javaClass != o.javaClass) false else animProp == (o as ValueAnimationOptions).animProp
62
+    }
63
+
64
+    fun equals(animationProperty: Property<View?, Float?>): Boolean {
65
+        return animProp!!.name == animationProperty.name
66
+    }
67
+
68
+    override fun hashCode(): Int {
69
+        return animProp.hashCode()
70
+    }
71
+
72
+    fun isAlpha(): Boolean = animProp == View.ALPHA
73
+
74
+    companion object {
75
+        fun parse(json: JSONObject?, property: Triple<Property<View, Float>?, Int?, (View) -> Float>): ValueAnimationOptions {
76
+            val options = ValueAnimationOptions()
77
+            options.animProp = property.first
78
+            options.animPropType = property.second
79
+            options.animationValueAccessor = property.third
80
+            options.from = FloatParser.parse(json, "from")
81
+            options.to = FloatParser.parse(json, "to")
82
+            options.duration = NumberParser.parse(json, "duration")
83
+            options.startDelay = NumberParser.parse(json, "startDelay")
84
+            options.interpolation = InterpolationParser.parse(json, "interpolation")
85
+            return options
86
+        }
87
+    }
88
+}

+ 6
- 4
lib/android/app/src/main/java/com/reactnativenavigation/presentation/Presenter.java View File

@@ -87,10 +87,11 @@ public class Presenter {
87 87
     }
88 88
 
89 89
     private void applyStatusBarOptions(Options options) {
90
-        setStatusBarBackgroundColor(options.statusBar);
91
-        setTextColorScheme(options.statusBar.textColorScheme);
92
-        setTranslucent(options.statusBar);
93
-        setStatusBarVisible(options.statusBar.visible);
90
+        StatusBarOptions statusBar = options.copy().withDefaultOptions(defaultOptions).statusBar;
91
+        setStatusBarBackgroundColor(statusBar);
92
+        setTextColorScheme(statusBar.textColorScheme);
93
+        setTranslucent(statusBar);
94
+        setStatusBarVisible(statusBar.visible);
94 95
     }
95 96
 
96 97
     private void setTranslucent(StatusBarOptions options) {
@@ -181,6 +182,7 @@ public class Presenter {
181 182
             } else {
182 183
                 flags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_FULLSCREEN;
183 184
             }
185
+            if (flags != view.getSystemUiVisibility()) view.requestLayout();
184 186
             view.setSystemUiVisibility(flags);
185 187
         } else if (drawBehind.hasValue()) {
186 188
             if (drawBehind.isTrue()) {

+ 0
- 52
lib/android/app/src/main/java/com/reactnativenavigation/react/ElementViewManager.java View File

@@ -1,52 +0,0 @@
1
-package com.reactnativenavigation.react;
2
-
3
-import com.facebook.react.uimanager.ThemedReactContext;
4
-import com.facebook.react.uimanager.ViewGroupManager;
5
-import com.facebook.react.uimanager.annotations.ReactProp;
6
-import com.reactnativenavigation.views.element.Element;
7
-
8
-import static com.reactnativenavigation.utils.UiUtils.runOnPreDrawOnce;
9
-import static com.reactnativenavigation.utils.ViewUtils.performOnParentReactView;
10
-
11
-public class ElementViewManager extends ViewGroupManager<Element> {
12
-
13
-    @Override
14
-    public String getName() {
15
-        return "RNNElement";
16
-    }
17
-
18
-    @Override
19
-    protected Element createViewInstance(ThemedReactContext reactContext) {
20
-        Element element = createView(reactContext);
21
-        register(element);
22
-        return element;
23
-    }
24
-
25
-    public Element createView(ThemedReactContext reactContext) {
26
-        return new Element(reactContext);
27
-    }
28
-
29
-    @Override
30
-    public void onDropViewInstance(Element element) {
31
-        super.onDropViewInstance(element);
32
-        unregister(element);
33
-    }
34
-
35
-    @ReactProp(name = "elementId")
36
-    public void setElementId(Element element, String id) {
37
-        element.setElementId(id);
38
-    }
39
-
40
-    @Override
41
-    public boolean needsCustomLayoutForChildren() {
42
-        return true;
43
-    }
44
-
45
-    private void register(Element element) {
46
-        runOnPreDrawOnce(element, () -> performOnParentReactView(element, (parent) -> parent.registerElement(element)));
47
-    }
48
-
49
-    private void unregister(Element element) {
50
-        performOnParentReactView(element, (parent) -> parent.unregisterElement(element));
51
-    }
52
-}

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

@@ -1,7 +1,5 @@
1 1
 package com.reactnativenavigation.react;
2 2
 
3
-import com.reactnativenavigation.react.events.EventEmitter;
4
-import com.reactnativenavigation.utils.LaunchArgsParser;
5 3
 import com.facebook.react.ReactInstanceManager;
6 4
 import com.facebook.react.bridge.Arguments;
7 5
 import com.facebook.react.bridge.Promise;
@@ -18,6 +16,8 @@ import com.reactnativenavigation.parse.LayoutNode;
18 16
 import com.reactnativenavigation.parse.Options;
19 17
 import com.reactnativenavigation.parse.parsers.JSONParser;
20 18
 import com.reactnativenavigation.parse.parsers.LayoutNodeParser;
19
+import com.reactnativenavigation.react.events.EventEmitter;
20
+import com.reactnativenavigation.utils.LaunchArgsParser;
21 21
 import com.reactnativenavigation.utils.NativeCommandListener;
22 22
 import com.reactnativenavigation.utils.Now;
23 23
 import com.reactnativenavigation.utils.StatusBarUtils;
@@ -125,7 +125,7 @@ public class NavigationModule extends ReactContextBaseJavaModule {
125 125
     @ReactMethod
126 126
     public void setStackRoot(String commandId, String onComponentId, ReadableArray children, Promise promise) {
127 127
         handle(() -> {
128
-            ArrayList<ViewController> _children = new ArrayList();
128
+            ArrayList<ViewController> _children = new ArrayList<>();
129 129
             for (int i = 0; i < children.size(); i++) {
130 130
                 final LayoutNode layoutTree = LayoutNodeParser.parse(jsonParser.parse(children.getMap(i)));
131 131
                 _children.add(layoutFactory.create(layoutTree));
@@ -194,12 +194,15 @@ public class NavigationModule extends ReactContextBaseJavaModule {
194 194
                null ? Options.EMPTY : Options.parse(new TypefaceLoader(activity()), jsonParser.parse(mergeOptions));
195 195
     }
196 196
 
197
-    private void handle(Runnable task) {
198
-        if (activity() == null || activity().isFinishing()) return;
199
-        UiThread.post(task);
197
+    protected void handle(Runnable task) {
198
+        UiThread.post(() -> {
199
+            if (getCurrentActivity() != null && !activity().isFinishing()) {
200
+                task.run();
201
+            }
202
+        });
200 203
     }
201 204
 
202
-    private NavigationActivity activity() {
205
+    protected NavigationActivity activity() {
203 206
         return (NavigationActivity) getCurrentActivity();
204 207
     }
205 208
 

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

@@ -7,6 +7,7 @@ import com.facebook.react.bridge.ReactApplicationContext;
7 7
 import com.facebook.react.uimanager.ViewManager;
8 8
 import com.reactnativenavigation.parse.LayoutFactory;
9 9
 
10
+import java.util.Collections;
10 11
 import java.util.List;
11 12
 
12 13
 import androidx.annotation.NonNull;
@@ -35,6 +36,6 @@ public class NavigationPackage implements ReactPackage {
35 36
     @NonNull
36 37
     @Override
37 38
     public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
38
-        return singletonList(new ElementViewManager());
39
+        return Collections.emptyList();
39 40
     }
40 41
 }

+ 5
- 26
lib/android/app/src/main/java/com/reactnativenavigation/react/ReactGateway.java View File

@@ -1,16 +1,10 @@
1 1
 package com.reactnativenavigation.react;
2 2
 
3 3
 import android.app.Activity;
4
-import android.app.Application;
5 4
 import android.content.Intent;
6 5
 
7 6
 import com.facebook.react.ReactNativeHost;
8
-import com.facebook.react.ReactPackage;
9
-import com.facebook.soloader.SoLoader;
10 7
 import com.reactnativenavigation.NavigationActivity;
11
-import com.reactnativenavigation.utils.Functions.FuncR;
12
-
13
-import java.util.List;
14 8
 
15 9
 public class ReactGateway {
16 10
 
@@ -18,30 +12,15 @@ public class ReactGateway {
18 12
 	private final NavigationReactInitializer initializer;
19 13
 	private final JsDevReloadHandler jsDevReloadHandler;
20 14
 
21
-    @SuppressWarnings("unused")
22
-    public ReactGateway(final Application application, final boolean isDebug, final List<ReactPackage> additionalReactPackages) {
23
-		this(application, isDebug, new NavigationReactNativeHost(application, isDebug, additionalReactPackages));
24
-	}
25
-
26
-    @SuppressWarnings("WeakerAccess")
27
-    public ReactGateway(final Application application, final boolean isDebug, final ReactNativeHost host) {
28
-        this(application, isDebug, () -> host);
29
-    }
30
-
31
-    public ReactGateway(final Application application, final boolean isDebug, FuncR<ReactNativeHost> hostCreator) {
32
-        SoLoader.init(application, false);
33
-        this.host = hostCreator.run();
34
-        initializer = new NavigationReactInitializer(host.getReactInstanceManager(), isDebug);
15
+    public ReactGateway(ReactNativeHost host) {
16
+        this.host = host;
17
+        initializer = new NavigationReactInitializer(host.getReactInstanceManager(), host.getUseDeveloperSupport());
35 18
         jsDevReloadHandler = new JsDevReloadHandler(host.getReactInstanceManager().getDevSupportManager());
36 19
         if (host instanceof BundleDownloadListenerProvider) {
37 20
             ((BundleDownloadListenerProvider) host).setBundleLoaderListener(jsDevReloadHandler);
38 21
         }
39 22
     }
40 23
 
41
-	public ReactNativeHost getReactNativeHost() {
42
-		return host;
43
-	}
44
-
45 24
 	public void onActivityCreated(NavigationActivity activity) {
46 25
 		initializer.onActivityCreated();
47 26
         jsDevReloadHandler.setReloadListener(activity);
@@ -53,8 +32,8 @@ public class ReactGateway {
53 32
 	}
54 33
 
55 34
     public boolean onNewIntent(Intent intent) {
56
-        if (getReactNativeHost().hasInstance()) {
57
-            getReactNativeHost().getReactInstanceManager().onNewIntent(intent);
35
+        if (host.hasInstance()) {
36
+            host.getReactInstanceManager().onNewIntent(intent);
58 37
             return true;
59 38
         }
60 39
         return false;

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

@@ -16,10 +16,6 @@ import com.reactnativenavigation.react.events.ComponentType;
16 16
 import com.reactnativenavigation.react.events.EventEmitter;
17 17
 import com.reactnativenavigation.viewcontrollers.IReactView;
18 18
 import com.reactnativenavigation.views.Renderable;
19
-import com.reactnativenavigation.views.element.Element;
20
-
21
-import java.util.ArrayList;
22
-import java.util.List;
23 19
 
24 20
 import androidx.annotation.RestrictTo;
25 21
 
@@ -31,7 +27,6 @@ public class ReactView extends ReactRootView implements IReactView, Renderable {
31 27
 	private final String componentName;
32 28
 	private boolean isAttachedToReactInstance = false;
33 29
     private final JSTouchDispatcher jsTouchDispatcher;
34
-    private ArrayList<Element> elements = new ArrayList<>();
35 30
 
36 31
     public ReactView(final Context context, ReactInstanceManager reactInstanceManager, String componentId, String componentName) {
37 32
 		super(context);
@@ -39,14 +34,17 @@ public class ReactView extends ReactRootView implements IReactView, Renderable {
39 34
 		this.componentId = componentId;
40 35
 		this.componentName = componentName;
41 36
 		jsTouchDispatcher = new JSTouchDispatcher(this);
42
-		start();
43 37
 	}
44 38
 
45
-	private void start() {
46
-		setEventListener(reactRootView -> {
47
-            reactRootView.setEventListener(null);
48
-            isAttachedToReactInstance = true;
49
-        });
39
+    @Override
40
+    protected void onAttachedToWindow() {
41
+        super.onAttachedToWindow();
42
+        start();
43
+    }
44
+
45
+    private void start() {
46
+        if (isAttachedToReactInstance) return;
47
+        isAttachedToReactInstance = true;
50 48
 		final Bundle opts = new Bundle();
51 49
 		opts.putString("componentId", componentId);
52 50
 		startReactApplication(reactInstanceManager, componentName, opts);
@@ -113,17 +111,4 @@ public class ReactView extends ReactRootView implements IReactView, Renderable {
113 111
     public String getComponentName() {
114 112
         return componentName;
115 113
     }
116
-
117
-    public void registerElement(Element element) {
118
-        elements.add(element);
119
-    }
120
-
121
-    public void unregisterElement(Element element) {
122
-        elements.remove(element);
123
-    }
124
-
125
-    @Override
126
-    public List<Element> getElements() {
127
-        return elements;
128
-    }
129 114
 }

+ 4
- 4
lib/android/app/src/main/java/com/reactnativenavigation/utils/CollectionUtils.java View File

@@ -67,13 +67,13 @@ public class CollectionUtils {
67 67
 
68 68
     public static <T> List<T> merge(@Nullable Collection<T> a, @Nullable Collection<T> b) {
69 69
         if (a == null && b == null) return null;
70
-        List<T> result = new ArrayList(get(a));
70
+        List<T> result = new ArrayList<>(get(a));
71 71
         result.addAll(get(b));
72 72
         return result;
73 73
     }
74 74
 
75 75
     public static <T> void forEach(@Nullable Collection<T> items, Apply<T> apply) {
76
-        if (items != null) forEach(new ArrayList(items), 0, apply);
76
+        if (items != null) forEach(new ArrayList<>(items), 0, apply);
77 77
     }
78 78
 
79 79
     public static <T> void forEach(@Nullable T[] items, Apply<T> apply) {
@@ -144,11 +144,11 @@ public class CollectionUtils {
144 144
     }
145 145
 
146 146
     public static @NonNull <T> Collection<T> get(@Nullable Collection<T> t) {
147
-        return t == null ? Collections.EMPTY_LIST : t;
147
+        return t == null ? Collections.emptyList() : t;
148 148
     }
149 149
 
150 150
     public static @NonNull <T> Collection<T> get(@Nullable Map<?, T> t) {
151
-        return t == null ? Collections.EMPTY_LIST : t.values();
151
+        return t == null ? Collections.emptyList() : t.values();
152 152
     }
153 153
 
154 154
     public static <T> boolean equals(@Nullable Collection<T> a, @Nullable Collection<T> b) {

+ 8
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/Context.kt View File

@@ -0,0 +1,8 @@
1
+package com.reactnativenavigation.utils
2
+
3
+import android.content.Context
4
+import com.facebook.react.ReactApplication
5
+
6
+fun Context.isDebug(): Boolean {
7
+    return (applicationContext as ReactApplication).reactNativeHost.useDeveloperSupport
8
+}

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

@@ -1,140 +0,0 @@
1
-package com.reactnativenavigation.utils;
2
-
3
-import android.app.Activity;
4
-import android.content.Context;
5
-import android.graphics.Bitmap;
6
-import android.graphics.BitmapFactory;
7
-import android.graphics.drawable.BitmapDrawable;
8
-import android.graphics.drawable.Drawable;
9
-import android.net.Uri;
10
-import android.os.StrictMode;
11
-import android.view.View;
12
-
13
-import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
14
-import com.reactnativenavigation.NavigationApplication;
15
-import com.reactnativenavigation.R;
16
-
17
-import java.io.FileNotFoundException;
18
-import java.io.IOException;
19
-import java.io.InputStream;
20
-import java.net.URL;
21
-import java.util.ArrayList;
22
-import java.util.List;
23
-
24
-import androidx.annotation.NonNull;
25
-import androidx.annotation.Nullable;
26
-import androidx.core.content.ContextCompat;
27
-
28
-public class ImageLoader {
29
-
30
-    public interface ImagesLoadingListener {
31
-        void onComplete(@NonNull List<Drawable> drawable);
32
-
33
-        void onComplete(@NonNull Drawable drawable);
34
-
35
-        void onError(Throwable error);
36
-    }
37
-
38
-    private static final String FILE_SCHEME = "file";
39
-
40
-    public Drawable getBackButtonIcon(Activity context) {
41
-        boolean isRTL = context.getWindow().getDecorView().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
42
-        return ContextCompat.getDrawable(context, isRTL ? R.drawable.ic_arrow_back_black_rtl_24dp : R.drawable.ic_arrow_back_black_24dp);
43
-    }
44
-
45
-    @Nullable
46
-    public Drawable loadIcon(Context context, @Nullable String uri) {
47
-        if (uri == null) return null;
48
-        try {
49
-            return getDrawable(context, uri);
50
-        } catch (IOException e) {
51
-            e.printStackTrace();
52
-        }
53
-        return null;
54
-    }
55
-
56
-    public void loadIcon(Context context, String uri, ImagesLoadingListener listener) {
57
-        try {
58
-            listener.onComplete(getDrawable(context, uri));
59
-        } catch (IOException e) {
60
-            listener.onError(e);
61
-        }
62
-    }
63
-
64
-    public void loadIcons(final Context context, List<String> uris, ImagesLoadingListener listener) {
65
-        try {
66
-            List<Drawable> drawables = new ArrayList<>();
67
-            for (String uri : uris) {
68
-                Drawable drawable = getDrawable(context, uri);
69
-                drawables.add(drawable);
70
-            }
71
-            listener.onComplete(drawables);
72
-        } catch (IOException e) {
73
-            listener.onError(e);
74
-        }
75
-    }
76
-
77
-    @NonNull
78
-    private Drawable getDrawable(Context context, @NonNull String source) throws IOException {
79
-        Drawable drawable;
80
-        if (isLocalFile(Uri.parse(source))) {
81
-            drawable = loadFile(source);
82
-        } else {
83
-            drawable = loadResource(source);
84
-            if (drawable == null && NavigationApplication.instance.isDebug()) {
85
-                drawable = readJsDevImage(context, source);
86
-            }
87
-        }
88
-        if (drawable == null) throw new RuntimeException("Could not load image " + source);
89
-        return drawable;
90
-    }
91
-
92
-    @NonNull
93
-    private Drawable readJsDevImage(Context context, String source) throws IOException {
94
-        StrictMode.ThreadPolicy threadPolicy = adjustThreadPolicyDebug();
95
-        InputStream is = openStream(context, source);
96
-        Bitmap bitmap = BitmapFactory.decodeStream(is);
97
-        restoreThreadPolicyDebug(threadPolicy);
98
-        return new BitmapDrawable(context.getResources(), bitmap);
99
-    }
100
-
101
-    private boolean isLocalFile(Uri uri) {
102
-        return FILE_SCHEME.equals(uri.getScheme());
103
-    }
104
-
105
-    private Drawable loadFile(String uri) {
106
-        Bitmap bitmap = BitmapFactory.decodeFile(Uri.parse(uri).getPath());
107
-        return new BitmapDrawable(NavigationApplication.instance.getResources(), bitmap);
108
-    }
109
-
110
-    private static Drawable loadResource(String iconSource) {
111
-        return ResourceDrawableIdHelper.getInstance().getResourceDrawable(NavigationApplication.instance, iconSource);
112
-    }
113
-
114
-    private StrictMode.ThreadPolicy adjustThreadPolicyDebug() {
115
-        StrictMode.ThreadPolicy threadPolicy = null;
116
-        if (NavigationApplication.instance.isDebug()) {
117
-            threadPolicy = StrictMode.getThreadPolicy();
118
-            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitNetwork().build());
119
-        }
120
-        return threadPolicy;
121
-    }
122
-
123
-    private void restoreThreadPolicyDebug(@Nullable StrictMode.ThreadPolicy threadPolicy) {
124
-        if (NavigationApplication.instance.isDebug() && threadPolicy != null) {
125
-            StrictMode.setThreadPolicy(threadPolicy);
126
-        }
127
-    }
128
-
129
-    private static InputStream openStream(Context context, String uri) throws IOException {
130
-        return uri.contains("http") ? remoteUrl(uri) : localFile(context, uri);
131
-    }
132
-
133
-    private static InputStream remoteUrl(String uri) throws IOException {
134
-        return new URL(uri).openStream();
135
-    }
136
-
137
-    private static InputStream localFile(Context context, String uri) throws FileNotFoundException {
138
-        return context.getContentResolver().openInputStream(Uri.parse(uri));
139
-    }
140
-}

+ 132
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/ImageLoader.kt View File

@@ -0,0 +1,132 @@
1
+package com.reactnativenavigation.utils
2
+
3
+import android.app.Activity
4
+import android.content.Context
5
+import android.graphics.BitmapFactory
6
+import android.graphics.drawable.BitmapDrawable
7
+import android.graphics.drawable.Drawable
8
+import android.net.Uri
9
+import android.os.StrictMode
10
+import android.view.View
11
+import androidx.core.content.ContextCompat
12
+import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper
13
+import com.reactnativenavigation.R
14
+import java.io.FileNotFoundException
15
+import java.io.IOException
16
+import java.io.InputStream
17
+import java.net.URL
18
+import java.util.*
19
+
20
+open class ImageLoader {
21
+    interface ImagesLoadingListener {
22
+        fun onComplete(drawable: List<Drawable>)
23
+        fun onComplete(drawable: Drawable)
24
+        fun onError(error: Throwable?)
25
+    }
26
+
27
+    open fun getBackButtonIcon(context: Activity): Drawable? {
28
+        val isRTL = context.window.decorView.layoutDirection == View.LAYOUT_DIRECTION_RTL
29
+        return ContextCompat.getDrawable(context, if (isRTL) R.drawable.ic_arrow_back_black_rtl_24dp else R.drawable.ic_arrow_back_black_24dp)
30
+    }
31
+
32
+    open fun loadIcon(context: Context, uri: String?): Drawable? {
33
+        if (uri == null) return null
34
+        try {
35
+            return getDrawable(context, uri)
36
+        } catch (e: IOException) {
37
+            e.printStackTrace()
38
+        }
39
+        return null
40
+    }
41
+
42
+    open fun loadIcon(context: Context, uri: String, listener: ImagesLoadingListener) {
43
+        try {
44
+            listener.onComplete(getDrawable(context, uri))
45
+        } catch (e: IOException) {
46
+            listener.onError(e)
47
+        }
48
+    }
49
+
50
+    open fun loadIcons(context: Context, uris: List<String>, listener: ImagesLoadingListener) {
51
+        try {
52
+            val drawables: MutableList<Drawable> = ArrayList()
53
+            for (uri in uris) {
54
+                val drawable = getDrawable(context, uri)
55
+                drawables.add(drawable)
56
+            }
57
+            listener.onComplete(drawables)
58
+        } catch (e: IOException) {
59
+            listener.onError(e)
60
+        }
61
+    }
62
+
63
+    @Throws(IOException::class)
64
+    private fun getDrawable(context: Context, source: String): Drawable {
65
+        var drawable: Drawable?
66
+        if (isLocalFile(Uri.parse(source))) {
67
+            drawable = loadFile(context, source)
68
+        } else {
69
+            drawable = loadResource(context, source)
70
+            if (drawable == null && context.isDebug()) {
71
+                drawable = readJsDevImage(context, source)
72
+            }
73
+        }
74
+        if (drawable == null) throw RuntimeException("Could not load image $source")
75
+        return drawable
76
+    }
77
+
78
+    @Throws(IOException::class)
79
+    private fun readJsDevImage(context: Context, source: String): Drawable {
80
+        val threadPolicy = adjustThreadPolicyDebug(context)
81
+        val `is` = openStream(context, source)
82
+        val bitmap = BitmapFactory.decodeStream(`is`)
83
+        restoreThreadPolicyDebug(context, threadPolicy)
84
+        return BitmapDrawable(context.resources, bitmap)
85
+    }
86
+
87
+    private fun isLocalFile(uri: Uri): Boolean {
88
+        return FILE_SCHEME == uri.scheme
89
+    }
90
+
91
+    private fun loadFile(context: Context, uri: String): Drawable {
92
+        val bitmap = BitmapFactory.decodeFile(Uri.parse(uri).path)
93
+        return BitmapDrawable(context.resources, bitmap)
94
+    }
95
+
96
+    private fun adjustThreadPolicyDebug(context: Context): StrictMode.ThreadPolicy? {
97
+        var threadPolicy: StrictMode.ThreadPolicy? = null
98
+        if (context.isDebug()) {
99
+            threadPolicy = StrictMode.getThreadPolicy()
100
+            StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder().permitNetwork().build())
101
+        }
102
+        return threadPolicy
103
+    }
104
+
105
+    private fun restoreThreadPolicyDebug(context: Context, threadPolicy: StrictMode.ThreadPolicy?) {
106
+        if (context.isDebug() && threadPolicy != null) {
107
+            StrictMode.setThreadPolicy(threadPolicy)
108
+        }
109
+    }
110
+
111
+    companion object {
112
+        private const val FILE_SCHEME = "file"
113
+        private fun loadResource(context: Context, iconSource: String): Drawable? {
114
+            return ResourceDrawableIdHelper.getInstance().getResourceDrawable(context, iconSource)
115
+        }
116
+
117
+        @Throws(IOException::class)
118
+        private fun openStream(context: Context, uri: String): InputStream? {
119
+            return if (uri.contains("http")) remoteUrl(uri) else localFile(context, uri)
120
+        }
121
+
122
+        @Throws(IOException::class)
123
+        private fun remoteUrl(uri: String): InputStream {
124
+            return URL(uri).openStream()
125
+        }
126
+
127
+        @Throws(FileNotFoundException::class)
128
+        private fun localFile(context: Context, uri: String): InputStream? {
129
+            return context.contentResolver.openInputStream(Uri.parse(uri))
130
+        }
131
+    }
132
+}

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

@@ -1,13 +1,14 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3 3
 import android.graphics.drawable.Drawable;
4
-import androidx.annotation.NonNull;
5 4
 
6 5
 import java.util.List;
7 6
 
7
+import androidx.annotation.NonNull;
8
+
8 9
 public class ImageLoadingListenerAdapter implements ImageLoader.ImagesLoadingListener {
9 10
     @Override
10
-    public void onComplete(@NonNull List<Drawable> drawables) {
11
+    public void onComplete(@NonNull List<? extends Drawable> drawables) {
11 12
 
12 13
     }
13 14
 

+ 3
- 3
lib/android/app/src/main/java/com/reactnativenavigation/utils/TextViewUtils.java View File

@@ -1,7 +1,5 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3
-import android.graphics.Color;
4
-import androidx.annotation.ColorInt;
5 3
 import android.text.SpannableString;
6 4
 import android.text.Spanned;
7 5
 import android.text.SpannedString;
@@ -9,12 +7,14 @@ import android.text.style.AbsoluteSizeSpan;
9 7
 import android.text.style.ForegroundColorSpan;
10 8
 import android.widget.TextView;
11 9
 
10
+import androidx.annotation.ColorInt;
11
+
12 12
 public class TextViewUtils {
13 13
     @ColorInt
14 14
     public static int getTextColor(TextView view) {
15 15
         SpannedString text = new SpannedString(view.getText());
16 16
         ForegroundColorSpan[] spans = text.getSpans(0, text.length(), ForegroundColorSpan.class);
17
-        return spans.length == 0 ? Color.WHITE : spans[0].getForegroundColor();
17
+        return spans.length == 0 ? view.getCurrentTextColor() : spans[0].getForegroundColor();
18 18
     }
19 19
 
20 20
     public static float getTextSize(TextView view) {

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

@@ -4,13 +4,14 @@ import android.content.Context;
4 4
 import android.content.res.Resources;
5 5
 import android.os.Handler;
6 6
 import android.os.Looper;
7
-import androidx.annotation.NonNull;
8
-import androidx.annotation.Nullable;
9 7
 import android.util.DisplayMetrics;
10 8
 import android.view.View;
11 9
 import android.view.ViewTreeObserver;
12 10
 import android.view.WindowManager;
13 11
 
12
+import androidx.annotation.NonNull;
13
+import androidx.annotation.Nullable;
14
+
14 15
 public class UiUtils {
15 16
     private static final int DEFAULT_TOOLBAR_HEIGHT = 56;
16 17
 
@@ -33,6 +34,17 @@ public class UiUtils {
33 34
         });
34 35
     }
35 36
 
37
+    public static void doOnLayout(@Nullable final View view, final Runnable task) {
38
+        if (view == null) return;
39
+        view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
40
+            @Override
41
+            public void onGlobalLayout() {
42
+                view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
43
+                task.run();
44
+            }
45
+        });
46
+    }
47
+
36 48
     public static void runOnMeasured(View view, Runnable task) {
37 49
         if (view.getHeight() > 0 && view.getWidth() > 0) {
38 50
             task.run();

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

@@ -5,7 +5,11 @@ import android.view.View;
5 5
 import androidx.annotation.NonNull;
6 6
 
7 7
 public class ViewTags {
8
-    public static @NonNull <T> T get(View view, int key, @NonNull T defaultValue) {
8
+    public static @NonNull <T> T get(View view, int key) {
9
+        return get(view, key, null);
10
+    }
11
+
12
+    public static @NonNull <T> T get(View view, int key, T defaultValue) {
9 13
         return view.getTag(key) == null ? defaultValue : (T) view.getTag(key);
10 14
     }
11 15
 }

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

@@ -1,19 +1,18 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3 3
 import android.graphics.Point;
4
-import androidx.annotation.Nullable;
5 4
 import android.view.View;
6 5
 import android.view.ViewGroup;
7 6
 import android.view.ViewManager;
8 7
 import android.view.ViewParent;
9 8
 
10 9
 import com.facebook.react.views.view.ReactViewBackgroundDrawable;
11
-import com.reactnativenavigation.react.ReactView;
12
-import com.reactnativenavigation.utils.Functions.Func1;
13 10
 
14 11
 import java.util.ArrayList;
15 12
 import java.util.List;
16 13
 
14
+import androidx.annotation.Nullable;
15
+
17 16
 import static com.reactnativenavigation.utils.ObjectUtils.perform;
18 17
 
19 18
 public class ViewUtils {
@@ -93,21 +92,14 @@ public class ViewUtils {
93 92
         return view.getLayoutParams().height < 0 ? view.getHeight() : view.getLayoutParams().height;
94 93
     }
95 94
 
96
-    public static void performOnParentReactView(View child, Func1<ReactView> task) {
97
-        ReactView parent = findParentReactView(child.getParent());
95
+    public static <T extends ViewGroup> T findParent(View view, Class<T> clazz) {
96
+        if (view == null) return null;
97
+        @Nullable ViewParent parent = view.getParent();
98 98
         if (parent != null) {
99
-            task.run(parent);
100
-        }
101
-    }
102
-
103
-    private static ReactView findParentReactView(ViewParent parent) {
104
-        if (parent == null) {
105
-            return null;
99
+            if (parent.getClass().isAssignableFrom(clazz)) return (T) parent;
100
+            return findParent((View) parent, clazz);
106 101
         }
107
-        if (parent instanceof ReactView) {
108
-            return (ReactView) parent;
109
-        }
110
-        return findParentReactView(parent.getParent());
102
+        return null;
111 103
     }
112 104
 
113 105
     public static Point getLocationOnScreen(View view) {
@@ -127,6 +119,12 @@ public class ViewUtils {
127 119
         return true;
128 120
     }
129 121
 
122
+    public static int getIndexInParent(View view) {
123
+        ViewParent parent = view.getParent();
124
+        if (parent == null) return -1;
125
+        return ((ViewGroup) parent).indexOfChild(view);
126
+    }
127
+
130 128
     public static int getBackgroundColor(View view) {
131 129
         if (view.getBackground() instanceof ReactViewBackgroundDrawable) {
132 130
             return ((ReactViewBackgroundDrawable) view.getBackground()).getColor();

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

@@ -8,6 +8,7 @@ import com.reactnativenavigation.parse.Options;
8 8
 import com.reactnativenavigation.presentation.Presenter;
9 9
 import com.reactnativenavigation.utils.StatusBarUtils;
10 10
 import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
11
+import com.reactnativenavigation.viewcontrollers.viewcontrolleroverlay.ViewControllerOverlay;
11 12
 import com.reactnativenavigation.views.Component;
12 13
 
13 14
 import androidx.annotation.CallSuper;
@@ -23,7 +24,7 @@ public abstract class ChildController<T extends ViewGroup> extends ViewControlle
23 24
     }
24 25
 
25 26
     public ChildController(Activity activity, ChildControllersRegistry childRegistry, String id, Presenter presenter, Options initialOptions) {
26
-        super(activity, id, new NoOpYellowBoxDelegate(), initialOptions);
27
+        super(activity, id, new NoOpYellowBoxDelegate(), initialOptions, new ViewControllerOverlay(activity));
27 28
         this.presenter = presenter;
28 29
         this.childRegistry = childRegistry;
29 30
     }

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

@@ -84,7 +84,7 @@ public class ComponentViewController extends ChildController<ComponentLayout> {
84 84
     @NonNull
85 85
     @Override
86 86
     protected ComponentLayout createView() {
87
-        view = (ComponentLayout) viewCreator.create(getActivity(), getId(), componentName);
87
+        ComponentLayout view = (ComponentLayout) viewCreator.create(getActivity(), getId(), componentName);
88 88
         return (ComponentLayout) view.asView();
89 89
     }
90 90
 

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

@@ -4,9 +4,6 @@ import android.view.MotionEvent;
4 4
 import android.view.View;
5 5
 
6 6
 import com.reactnativenavigation.interfaces.ScrollEventListener;
7
-import com.reactnativenavigation.views.element.Element;
8
-
9
-import java.util.List;
10 7
 
11 8
 public interface IReactView extends Destroyable {
12 9
 
@@ -21,6 +18,4 @@ public interface IReactView extends Destroyable {
21 18
     void dispatchTouchEventToJs(MotionEvent event);
22 19
 
23 20
     boolean isRendered();
24
-
25
-    List<Element> getElements();
26 21
 }

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

@@ -20,6 +20,7 @@ import com.reactnativenavigation.utils.ImageLoadingListenerAdapter;
20 20
 import com.reactnativenavigation.utils.UiUtils;
21 21
 import com.reactnativenavigation.utils.ViewUtils;
22 22
 import com.reactnativenavigation.viewcontrollers.button.IconResolver;
23
+import com.reactnativenavigation.viewcontrollers.viewcontrolleroverlay.ViewControllerOverlay;
23 24
 import com.reactnativenavigation.views.titlebar.TitleBarReactButtonView;
24 25
 
25 26
 import java.util.List;
@@ -57,7 +58,7 @@ public class TitleBarButtonController extends ViewController<TitleBarReactButton
57 58
                                     Button button,
58 59
                                     ReactViewCreator viewCreator,
59 60
                                     OnClickListener onClickListener) {
60
-        super(activity, button.id, new YellowBoxDelegate(), new Options());
61
+        super(activity, button.id, new YellowBoxDelegate(), new Options(), new ViewControllerOverlay(activity));
61 62
         this.navigationIconResolver = navigationIconResolver;
62 63
         this.optionsPresenter = optionsPresenter;
63 64
         this.button = button;

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

@@ -6,6 +6,7 @@ import com.reactnativenavigation.parse.Component;
6 6
 import com.reactnativenavigation.parse.Options;
7 7
 import com.reactnativenavigation.react.events.ComponentType;
8 8
 import com.reactnativenavigation.utils.CompatUtils;
9
+import com.reactnativenavigation.viewcontrollers.viewcontrolleroverlay.ViewControllerOverlay;
9 10
 import com.reactnativenavigation.views.titlebar.TitleBarReactView;
10 11
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
11 12
 
@@ -15,7 +16,7 @@ public class TitleBarReactViewController extends ViewController<TitleBarReactVie
15 16
     private Component component;
16 17
 
17 18
     public TitleBarReactViewController(Activity activity, TitleBarReactViewCreator reactViewCreator) {
18
-        super(activity, CompatUtils.generateViewId() + "", new YellowBoxDelegate(), new Options());
19
+        super(activity, CompatUtils.generateViewId() + "", new YellowBoxDelegate(), new Options(), new ViewControllerOverlay(activity));
19 20
         this.reactViewCreator = reactViewCreator;
20 21
     }
21 22
 

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

@@ -17,13 +17,12 @@ import com.reactnativenavigation.utils.StringUtils;
17 17
 import com.reactnativenavigation.utils.UiThread;
18 18
 import com.reactnativenavigation.utils.UiUtils;
19 19
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
20
+import com.reactnativenavigation.viewcontrollers.viewcontrolleroverlay.ViewControllerOverlay;
20 21
 import com.reactnativenavigation.views.BehaviourAdapter;
21 22
 import com.reactnativenavigation.views.Component;
22 23
 import com.reactnativenavigation.views.Renderable;
23
-import com.reactnativenavigation.views.element.Element;
24 24
 
25 25
 import java.util.ArrayList;
26
-import java.util.Collections;
27 26
 import java.util.List;
28 27
 
29 28
 import androidx.annotation.CallSuper;
@@ -40,7 +39,7 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
40 39
         ViewGroup.OnHierarchyChangeListener,
41 40
         BehaviourAdapter {
42 41
 
43
-    private final List<Runnable> onAppearedListeners = new ArrayList();
42
+    private final List<Runnable> onAppearedListeners = new ArrayList<>();
44 43
     private boolean appearEventPosted;
45 44
     private boolean isFirstLayout = true;
46 45
     private Bool waitForRender = new NullBool();
@@ -69,17 +68,19 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
69 68
     private boolean isDestroyed;
70 69
     private ViewVisibilityListener viewVisibilityListener = new ViewVisibilityListenerAdapter();
71 70
     protected FabPresenter fabOptionsPresenter;
71
+    private ViewControllerOverlay overlay;
72 72
 
73 73
     public boolean isDestroyed() {
74 74
         return isDestroyed;
75 75
     }
76 76
 
77
-    public ViewController(Activity activity, String id, YellowBoxDelegate yellowBoxDelegate, Options initialOptions) {
77
+    public ViewController(Activity activity, String id, YellowBoxDelegate yellowBoxDelegate, Options initialOptions, ViewControllerOverlay overlay) {
78 78
         this.activity = activity;
79 79
         this.id = id;
80 80
         this.yellowBoxDelegate = yellowBoxDelegate;
81 81
         fabOptionsPresenter = new FabPresenter();
82 82
         this.initialOptions = initialOptions;
83
+        this.overlay = overlay;
83 84
         options = initialOptions.copy();
84 85
     }
85 86
 
@@ -92,7 +93,11 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
92 93
     }
93 94
 
94 95
     public void addOnAppearedListener(Runnable onAppearedListener) {
95
-        onAppearedListeners.add(onAppearedListener);
96
+        if (isShown) {
97
+            onAppearedListener.run();
98
+        } else {
99
+            onAppearedListeners.add(onAppearedListener);
100
+        }
96 101
     }
97 102
 
98 103
     public void removeOnAppearedListener(Runnable onAppearedListener) {
@@ -114,6 +119,14 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
114 119
         return false;
115 120
     }
116 121
 
122
+    public void addOverlay(View v) {
123
+        perform(view, view -> overlay.add(view, v));
124
+    }
125
+
126
+    public void removeOverlay(View view) {
127
+        overlay.remove(view);
128
+    }
129
+
117 130
     @CheckResult
118 131
     public Options resolveCurrentOptions() {
119 132
         return options;
@@ -140,7 +153,7 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
140 153
     }
141 154
 
142 155
     public void setDefaultOptions(Options defaultOptions) {
143
-        
156
+
144 157
     }
145 158
 
146 159
     public Activity getActivity() {
@@ -160,6 +173,10 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
160 173
         return parentController;
161 174
     }
162 175
 
176
+    public ParentController requireParentController() {
177
+        return parentController;
178
+    }
179
+
163 180
     public void setParentController(@NonNull final ParentController parentController) {
164 181
         this.parentController = parentController;
165 182
     }
@@ -325,10 +342,6 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
325 342
         if (controller != null) task.run(controller);
326 343
     }
327 344
 
328
-    public List<Element> getElements() {
329
-        return getView() instanceof IReactView && view != null? ((IReactView) view).getElements() : Collections.EMPTY_LIST;
330
-    }
331
-
332 345
     @Override
333 346
     @CallSuper
334 347
     public boolean onMeasureChild(CoordinatorLayout parent, ViewGroup child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {

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

@@ -13,6 +13,7 @@ import com.reactnativenavigation.utils.CoordinatorLayoutUtils;
13 13
 import com.reactnativenavigation.utils.StatusBarUtils;
14 14
 import com.reactnativenavigation.viewcontrollers.NoOpYellowBoxDelegate;
15 15
 import com.reactnativenavigation.viewcontrollers.ViewController;
16
+import com.reactnativenavigation.viewcontrollers.viewcontrolleroverlay.ViewControllerOverlay;
16 17
 import com.reactnativenavigation.views.BehaviourDelegate;
17 18
 import com.reactnativenavigation.views.ExternalComponentLayout;
18 19
 
@@ -29,7 +30,7 @@ public class ExternalComponentViewController extends ViewController<ExternalComp
29 30
     private final ExternalComponentPresenter presenter;
30 31
 
31 32
     public ExternalComponentViewController(Activity activity, String id, ExternalComponent externalComponent, ExternalComponentCreator componentCreator, ReactInstanceManager reactInstanceManager, EventEmitter emitter, ExternalComponentPresenter presenter, Options initialOptions) {
32
-        super(activity, id, new NoOpYellowBoxDelegate(), initialOptions);
33
+        super(activity, id, new NoOpYellowBoxDelegate(), initialOptions, new ViewControllerOverlay(activity));
33 34
         this.externalComponent = externalComponent;
34 35
         this.componentCreator = componentCreator;
35 36
         this.reactInstanceManager = reactInstanceManager;

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

@@ -1,8 +1,6 @@
1 1
 package com.reactnativenavigation.viewcontrollers.modal;
2 2
 
3 3
 import android.app.Activity;
4
-import androidx.annotation.RestrictTo;
5
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
6 4
 import android.view.ViewGroup;
7 5
 
8 6
 import com.reactnativenavigation.anim.ModalAnimator;
@@ -18,6 +16,9 @@ import java.util.List;
18 16
 
19 17
 import javax.annotation.Nullable;
20 18
 
19
+import androidx.annotation.RestrictTo;
20
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
21
+
21 22
 public class ModalStack {
22 23
     private List<ViewController> modals = new ArrayList<>();
23 24
     private final ModalPresenter presenter;

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

@@ -34,9 +34,7 @@ public class Navigator extends ParentController {
34 34
     private final OverlayManager overlayManager;
35 35
     private final RootPresenter rootPresenter;
36 36
     private ViewController root;
37
-
38 37
     private ViewController previousRoot;
39
-
40 38
     private final CoordinatorLayout rootLayout;
41 39
     private final CoordinatorLayout modalsLayout;
42 40
     private final CoordinatorLayout overlaysLayout;

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

@@ -5,6 +5,7 @@ import android.view.View;
5 5
 import android.view.ViewGroup;
6 6
 
7 7
 import com.reactnativenavigation.anim.NavigationAnimator;
8
+import com.reactnativenavigation.parse.NestedAnimationsOptions;
8 9
 import com.reactnativenavigation.parse.Options;
9 10
 import com.reactnativenavigation.presentation.Presenter;
10 11
 import com.reactnativenavigation.presentation.StackPresenter;
@@ -155,23 +156,15 @@ public class StackController extends ParentController<StackLayout> {
155 156
         child.setParentController(this);
156 157
         stack.push(child.getId(), child);
157 158
         Options resolvedOptions = resolveCurrentOptions(presenter.getDefaultOptions());
158
-        addChildToStack(child, child.getView(), resolvedOptions);
159
+        addChildToStack(child, resolvedOptions);
159 160
 
160 161
         if (toRemove != null) {
161
-            if (resolvedOptions.animations.push.enabled.isTrueOrUndefined()) {
162
-                if (resolvedOptions.animations.push.waitForRender.isTrue()) {
163
-                    child.getView().setAlpha(0);
164
-                    child.addOnAppearedListener(() -> animator.push(child.getView(), resolvedOptions.animations.push, resolvedOptions.transitions, toRemove.getElements(), child.getElements(), () -> {
165
-                        getView().removeView(toRemove.getView());
166
-                        listener.onSuccess(child.getId());
167
-                    }));
162
+            NestedAnimationsOptions animation = resolvedOptions.animations.push;
163
+            if (animation.enabled.isTrueOrUndefined()) {
164
+                if (animation.waitForRender.isTrue() || resolvedOptions.animations.push.sharedElements.hasValue()) {
165
+                    animator.push(child, toRemove, resolvedOptions, () -> onPushAnimationComplete(child, toRemove, listener));
168 166
                 } else {
169
-                    animator.push(child.getView(), resolvedOptions.animations.push, () -> {
170
-                        if (!toRemove.equals(peek())) {
171
-                            getView().removeView(toRemove.getView());
172
-                        }
173
-                        listener.onSuccess(child.getId());
174
-                    });
167
+                    animator.push(child, toRemove, resolvedOptions, () -> onPushAnimationComplete(child, toRemove, listener));
175 168
                 }
176 169
             } else {
177 170
                 getView().removeView(toRemove.getView());
@@ -182,10 +175,15 @@ public class StackController extends ParentController<StackLayout> {
182 175
         }
183 176
     }
184 177
 
185
-    private void addChildToStack(ViewController child, View view, Options resolvedOptions) {
178
+    private void onPushAnimationComplete(ViewController toAdd, ViewController toRemove, CommandListener listener) {
179
+        if (!peek().equals(toRemove)) getView().removeView(toRemove.getView());
180
+        listener.onSuccess(toAdd.getId());
181
+    }
182
+
183
+    private void addChildToStack(ViewController child, Options resolvedOptions) {
186 184
         child.setWaitForRender(resolvedOptions.animations.push.waitForRender);
187 185
         if (size() == 1) presenter.applyInitialChildLayoutOptions(resolvedOptions);
188
-        getView().addView(view, getView().getChildCount() - 1, matchParentWithBehaviour(new StackBehaviour(this)));
186
+        getView().addView(child.getView(), getView().getChildCount() - 1, matchParentWithBehaviour(new StackBehaviour(this)));
189 187
     }
190 188
 
191 189
     public void setRoot(List<ViewController> children, CommandListener listener) {
@@ -204,7 +202,7 @@ public class StackController extends ParentController<StackLayout> {
204 202
         child.setParentController(this);
205 203
         stack.push(child.getId(), child);
206 204
         Options resolvedOptions = resolveCurrentOptions(presenter.getDefaultOptions());
207
-        addChildToStack(child, child.getView(), resolvedOptions);
205
+        addChildToStack(child, resolvedOptions);
208 206
 
209 207
         CommandListener listenerAdapter = new CommandListenerAdapter() {
210 208
             @Override
@@ -229,16 +227,14 @@ public class StackController extends ParentController<StackLayout> {
229 227
             if (resolvedOptions.animations.setStackRoot.waitForRender.isTrue()) {
230 228
                 child.getView().setAlpha(0);
231 229
                 child.addOnAppearedListener(() -> animator.push(
232
-                        child.getView(),
233
-                        resolvedOptions.animations.setStackRoot,
234
-                        resolvedOptions.transitions,
235
-                        toRemove.getElements(),
236
-                        child.getElements(),
230
+                        child,
231
+                        toRemove,
232
+                        resolvedOptions,
237 233
                         () -> listenerAdapter.onSuccess(child.getId())
238 234
                     )
239 235
                 );
240 236
             } else {
241
-                animator.push(child.getView(), resolvedOptions.animations.setStackRoot, () -> listenerAdapter.onSuccess(child.getId()));
237
+                animator.push(child, toRemove, resolvedOptions, () -> listenerAdapter.onSuccess(child.getId()));
242 238
             }
243 239
         } else {
244 240
             listenerAdapter.onSuccess(child.getId());

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

@@ -8,6 +8,7 @@ import com.reactnativenavigation.react.events.ComponentType;
8 8
 import com.reactnativenavigation.utils.CompatUtils;
9 9
 import com.reactnativenavigation.viewcontrollers.ViewController;
10 10
 import com.reactnativenavigation.viewcontrollers.YellowBoxDelegate;
11
+import com.reactnativenavigation.viewcontrollers.viewcontrolleroverlay.ViewControllerOverlay;
11 12
 import com.reactnativenavigation.views.topbar.TopBarBackgroundView;
12 13
 import com.reactnativenavigation.views.topbar.TopBarBackgroundViewCreator;
13 14
 
@@ -17,7 +18,7 @@ public class TopBarBackgroundViewController extends ViewController<TopBarBackgro
17 18
     private Component component;
18 19
 
19 20
     public TopBarBackgroundViewController(Activity activity, TopBarBackgroundViewCreator viewCreator) {
20
-        super(activity, CompatUtils.generateViewId() + "", new YellowBoxDelegate(), new Options());
21
+        super(activity, CompatUtils.generateViewId() + "", new YellowBoxDelegate(), new Options(), new ViewControllerOverlay(activity));
21 22
         this.viewCreator = viewCreator;
22 23
     }
23 24
 

+ 28
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontrolleroverlay/ViewControllerOverlay.kt View File

@@ -0,0 +1,28 @@
1
+package com.reactnativenavigation.viewcontrollers.viewcontrolleroverlay
2
+
3
+import android.content.Context
4
+import android.view.View
5
+import android.view.ViewGroup
6
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
7
+import android.widget.FrameLayout
8
+import com.reactnativenavigation.utils.ViewUtils.removeFromParent
9
+
10
+class ViewControllerOverlay(context: Context) {
11
+    private val overlay: FrameLayout = FrameLayout(context)
12
+
13
+    fun add(parent: ViewGroup, view: View) {
14
+        attachOverlayToParent(parent)
15
+        overlay.addView(view)
16
+    }
17
+
18
+    fun remove(view: View) {
19
+        overlay.removeView(view)
20
+        if (overlay.childCount == 0) removeFromParent(overlay)
21
+    }
22
+
23
+    private fun attachOverlayToParent(parent: ViewGroup) {
24
+        if (overlay.parent == null) {
25
+            parent.addView(overlay, MATCH_PARENT, MATCH_PARENT)
26
+        }
27
+    }
28
+}

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

@@ -11,11 +11,8 @@ import com.reactnativenavigation.parse.params.Bool;
11 11
 import com.reactnativenavigation.react.ReactView;
12 12
 import com.reactnativenavigation.react.events.ComponentType;
13 13
 import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
14
-import com.reactnativenavigation.views.element.Element;
15 14
 import com.reactnativenavigation.views.touch.OverlayTouchDelegate;
16 15
 
17
-import java.util.List;
18
-
19 16
 import androidx.coordinatorlayout.widget.CoordinatorLayout;
20 17
 
21 18
 import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentLP;
@@ -84,11 +81,6 @@ public class ComponentLayout extends CoordinatorLayout implements ReactComponent
84 81
         return reactView.isRendered();
85 82
     }
86 83
 
87
-    @Override
88
-    public List<Element> getElements() {
89
-        return reactView.getElements();
90
-    }
91
-
92 84
     @Override
93 85
     public void onPress(String buttonId) {
94 86
         reactView.sendOnNavigationButtonPressed(buttonId);

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

@@ -4,7 +4,6 @@ import android.content.Context;
4 4
 import android.graphics.PorterDuff;
5 5
 import android.graphics.PorterDuffColorFilter;
6 6
 import android.graphics.drawable.Drawable;
7
-import androidx.annotation.NonNull;
8 7
 
9 8
 import com.github.clans.fab.FloatingActionButton;
10 9
 import com.reactnativenavigation.anim.FabAnimator;
@@ -17,6 +16,8 @@ import com.reactnativenavigation.utils.ImageLoadingListenerAdapter;
17 16
 import java.util.Collections;
18 17
 import java.util.List;
19 18
 
19
+import androidx.annotation.NonNull;
20
+
20 21
 
21 22
 public class Fab extends FloatingActionButton implements FabAnimator {
22 23
 
@@ -32,7 +33,7 @@ public class Fab extends FloatingActionButton implements FabAnimator {
32 33
     public void applyIcon(String icon, Colour color) {
33 34
         new ImageLoader().loadIcons(getContext(), Collections.singletonList(icon), new ImageLoadingListenerAdapter() {
34 35
             @Override
35
-            public void onComplete(@NonNull List<Drawable> drawables) {
36
+            public void onComplete(@NonNull List<? extends Drawable> drawables) {
36 37
                 if (color.hasValue()) drawables.get(0).setColorFilter(new PorterDuffColorFilter(color.get(), PorterDuff.Mode.SRC_IN));
37 38
                 setImageDrawable(drawables.get(0));
38 39
             }

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

@@ -1,85 +0,0 @@
1
-package com.reactnativenavigation.views.element;
2
-
3
-import android.content.Context;
4
-import android.graphics.Rect;
5
-import androidx.annotation.Keep;
6
-import androidx.annotation.NonNull;
7
-import androidx.annotation.Nullable;
8
-import android.text.SpannableString;
9
-import android.text.SpannedString;
10
-import android.view.View;
11
-import android.widget.FrameLayout;
12
-import android.widget.TextView;
13
-
14
-import com.facebook.drawee.drawable.ScalingUtils;
15
-import com.facebook.drawee.generic.GenericDraweeHierarchy;
16
-import com.facebook.drawee.view.DraweeView;
17
-import com.facebook.react.views.view.ReactViewBackgroundDrawable;
18
-import com.reactnativenavigation.utils.TextViewUtils;
19
-import com.reactnativenavigation.utils.UiUtils;
20
-
21
-import static com.reactnativenavigation.utils.ColorUtils.labToColor;
22
-import static com.reactnativenavigation.utils.TextViewUtils.setColor;
23
-
24
-public class Element extends FrameLayout {
25
-    private String elementId;
26
-    @Nullable private SpannableString spannableText;
27
-    private float originalTextSize;
28
-
29
-    public Element(@NonNull Context context) {
30
-        super(context);
31
-    }
32
-
33
-    public String getElementId() {
34
-        return elementId;
35
-    }
36
-
37
-    public void setElementId(String elementId) {
38
-        this.elementId = elementId;
39
-    }
40
-
41
-    public View getChild() {
42
-        return getChildAt(0);
43
-    }
44
-
45
-    @Override
46
-    public void onViewAdded(View child) {
47
-        super.onViewAdded(child);
48
-        UiUtils.runOnPreDrawOnce(child, () -> {
49
-            if (child instanceof TextView) {
50
-                SpannedString spannedText = new SpannedString(((TextView) child).getText());
51
-                spannableText = new SpannableString(spannedText);
52
-                ((TextView) child).setText(spannableText);
53
-
54
-                originalTextSize = TextViewUtils.getTextSize((TextView) child);
55
-            }
56
-        });
57
-    }
58
-
59
-    @Keep
60
-    public void setClipBounds(Rect clipBounds) {
61
-        getChild().setClipBounds(clipBounds);
62
-    }
63
-
64
-    @Keep
65
-    public void setMatrixTransform(float value) {
66
-        GenericDraweeHierarchy hierarchy = ((DraweeView<GenericDraweeHierarchy>) getChild()).getHierarchy();
67
-        if (hierarchy.getActualImageScaleType() != null) {
68
-            ((ScalingUtils.InterpolatingScaleType) hierarchy.getActualImageScaleType()).setValue(value);
69
-            getChild().invalidate();
70
-        }
71
-    }
72
-
73
-    @Keep
74
-    public void setBackgroundColor(double[] color) {
75
-        ((ReactViewBackgroundDrawable) getChild().getBackground()).setColor(labToColor(color));
76
-    }
77
-
78
-    @Keep
79
-    public void setTextColor(double[] color) {
80
-        if (spannableText != null) {
81
-            setColor(spannableText, labToColor(color));
82
-            ((TextView) getChild()).setText(spannableText);
83
-        }
84
-    }
85
-}

+ 19
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/element/ElementTransition.kt View File

@@ -0,0 +1,19 @@
1
+package com.reactnativenavigation.views.element
2
+
3
+import android.animation.AnimatorSet
4
+import android.view.View
5
+import com.reactnativenavigation.parse.ElementTransitionOptions
6
+import com.reactnativenavigation.viewcontrollers.ViewController
7
+
8
+class ElementTransition(private val transitionOptions: ElementTransitionOptions) : Transition() {
9
+    val id: String
10
+        get() = transitionOptions.id
11
+    override lateinit var viewController: ViewController<*>
12
+    override lateinit var view: View
13
+    override val topInset: Int
14
+        get() = viewController.topInset
15
+
16
+    override fun createAnimators(): AnimatorSet = transitionOptions.getAnimation(view)
17
+
18
+    fun isValid(): Boolean = ::view.isInitialized
19
+}

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

@@ -1,41 +0,0 @@
1
-package com.reactnativenavigation.views.element;
2
-
3
-import android.animation.Animator;
4
-import androidx.annotation.RestrictTo;
5
-
6
-import com.reactnativenavigation.parse.Transition;
7
-import com.reactnativenavigation.parse.Transitions;
8
-
9
-import java.util.Collection;
10
-import java.util.Collections;
11
-import java.util.List;
12
-import java.util.Map;
13
-
14
-import static com.reactnativenavigation.utils.CollectionUtils.filter;
15
-import static com.reactnativenavigation.utils.CollectionUtils.keyBy;
16
-
17
-public class ElementTransitionManager {
18
-
19
-    private final TransitionValidator validator;
20
-    private final TransitionAnimatorCreator animatorCreator;
21
-
22
-    @RestrictTo(RestrictTo.Scope.TESTS)
23
-    ElementTransitionManager(TransitionValidator validator, TransitionAnimatorCreator animatorCreator) {
24
-        this.validator = validator;
25
-        this.animatorCreator = animatorCreator;
26
-    }
27
-
28
-    public ElementTransitionManager() {
29
-        validator = new TransitionValidator();
30
-        animatorCreator = new TransitionAnimatorCreator();
31
-    }
32
-
33
-    public Collection<Animator> createTransitions(Transitions transitions, List<Element> fromElements, List<Element> toElements) {
34
-        if (!transitions.hasValue() || fromElements.isEmpty() || toElements.isEmpty()) return Collections.EMPTY_LIST;
35
-        Map<String, Element> from = keyBy(fromElements, Element::getElementId);
36
-        Map<String, Element> to = keyBy(toElements, Element::getElementId);
37
-        List<Transition> validTransitions = filter(transitions.get(), (t) -> validator.validate(t, from, to));
38
-
39
-        return animatorCreator.create(validTransitions, from, to);
40
-    }
41
-}

+ 74
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/element/ElementTransitionManager.kt View File

@@ -0,0 +1,74 @@
1
+package com.reactnativenavigation.views.element
2
+
3
+import android.animation.AnimatorSet
4
+import android.view.View
5
+import androidx.core.view.doOnLayout
6
+import com.facebook.react.uimanager.util.ReactFindViewUtil.OnViewFoundListener
7
+import com.facebook.react.uimanager.util.ReactFindViewUtil.findView
8
+import com.reactnativenavigation.parse.AnimationOptions
9
+import com.reactnativenavigation.parse.NestedAnimationsOptions
10
+import com.reactnativenavigation.utils.Functions.Func1
11
+import com.reactnativenavigation.viewcontrollers.ViewController
12
+
13
+open class ElementTransitionManager {
14
+    private val animatorCreator: TransitionAnimatorCreator = TransitionAnimatorCreator()
15
+
16
+    fun createTransitions(animation: NestedAnimationsOptions, fromScreen: ViewController<*>, toScreen: ViewController<*>, onAnimatorsCreated: Func1<TransitionSet?>) {
17
+        val sharedElements = animation.sharedElements
18
+        val elementTransitions = animation.elementTransitions
19
+        if (!sharedElements.hasValue() && !elementTransitions.hasValue) {
20
+            onAnimatorsCreated.run(TransitionSet())
21
+            return
22
+        }
23
+        val transitionSet = TransitionSet()
24
+        for (transitionOptions in sharedElements.get()) {
25
+            val transition = SharedElementTransition(toScreen, transitionOptions!!)
26
+            findView(fromScreen.view, transition.fromId)?.let { transition.from = it }
27
+            findView(toScreen.view, object : OnViewFoundListener {
28
+                override fun getNativeId(): String {
29
+                    return transition.toId
30
+                }
31
+
32
+                override fun onViewFound(view: View) {
33
+                    view.doOnLayout {
34
+                        transition.to = view
35
+                        if (transition.isValid()) transitionSet.add(transition)
36
+                        if (transitionSet.size() == sharedElements.get().size + elementTransitions.transitions.size) {
37
+                            onAnimatorsCreated.run(transitionSet)
38
+                        }
39
+                    }
40
+                }
41
+            })
42
+        }
43
+        for (transitionOptions in elementTransitions.transitions) {
44
+            val transition = ElementTransition(transitionOptions)
45
+            findView(fromScreen.view, transition.id)?.let {
46
+                transition.view = it
47
+                transition.viewController = fromScreen
48
+                transitionSet.add(transition)
49
+            }
50
+            if (transition.isValid()) continue
51
+            findView(toScreen.view, object : OnViewFoundListener {
52
+                override fun getNativeId(): String {
53
+                    return transition.id
54
+                }
55
+
56
+                override fun onViewFound(view: View) {
57
+                    view.doOnLayout {
58
+                        transition.view = view
59
+                        transition.viewController = toScreen
60
+                        transitionSet.add(transition)
61
+                        if (transitionSet.size() == sharedElements.get().size + elementTransitions.transitions.size) {
62
+                            onAnimatorsCreated.run(transitionSet)
63
+                        }
64
+                    }
65
+                }
66
+            })
67
+        }
68
+    }
69
+
70
+    fun createAnimators(fadeAnimation: AnimationOptions?, transitionSet: TransitionSet?): AnimatorSet {
71
+        return animatorCreator.create(fadeAnimation!!, transitionSet!!)
72
+    }
73
+
74
+}

+ 42
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/element/SharedElementTransition.kt View File

@@ -0,0 +1,42 @@
1
+package com.reactnativenavigation.views.element
2
+
3
+import android.animation.AnimatorSet
4
+import android.view.View
5
+import com.reactnativenavigation.parse.SharedElementTransitionOptions
6
+import com.reactnativenavigation.viewcontrollers.ViewController
7
+import com.reactnativenavigation.views.element.animators.*
8
+
9
+class SharedElementTransition(appearing: ViewController<*>, private val options: SharedElementTransitionOptions) : Transition() {
10
+    val fromId: String = options.fromId.get()
11
+    val toId: String = options.toId.get()
12
+    lateinit var from: View
13
+    lateinit var to: View
14
+    override var viewController: ViewController<*> = appearing
15
+    override val view: View
16
+        get() = to
17
+    override val topInset: Int
18
+        get() = viewController.topInset
19
+
20
+    fun isValid(): Boolean = this::from.isInitialized
21
+
22
+    override fun createAnimators(): AnimatorSet {
23
+        val animators = animators()
24
+                .filter { it.shouldAnimateProperty() }
25
+                .map { it.create(options) }
26
+        val set = AnimatorSet()
27
+        set.playTogether(animators)
28
+        return set
29
+    }
30
+
31
+    private fun animators(): List<PropertyAnimatorCreator<*>> {
32
+        return listOf(
33
+                XAnimator(from, to),
34
+                YAnimator(from, to),
35
+                MatrixAnimator(from, to),
36
+                ScaleXAnimator(from, to),
37
+                ScaleYAnimator(from, to),
38
+                BackgroundColorAnimator(from, to),
39
+                TextChangeAnimator(from, to)
40
+        )
41
+    }
42
+}

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

@@ -0,0 +1,13 @@
1
+package com.reactnativenavigation.views.element
2
+
3
+import android.animation.AnimatorSet
4
+import android.view.View
5
+import com.reactnativenavigation.viewcontrollers.ViewController
6
+
7
+abstract class Transition {
8
+    abstract var viewController: ViewController<*>
9
+    abstract val view: View
10
+    abstract val topInset: Int
11
+
12
+    abstract fun createAnimators(): AnimatorSet
13
+}

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

@@ -1,52 +0,0 @@
1
-package com.reactnativenavigation.views.element;
2
-
3
-import android.animation.Animator;
4
-
5
-import com.reactnativenavigation.parse.Transition;
6
-import com.reactnativenavigation.views.element.animators.BackgroundColorAnimator;
7
-import com.reactnativenavigation.views.element.animators.MatrixAnimator;
8
-import com.reactnativenavigation.views.element.animators.PropertyAnimatorCreator;
9
-import com.reactnativenavigation.views.element.animators.ScaleXAnimator;
10
-import com.reactnativenavigation.views.element.animators.ScaleYAnimator;
11
-import com.reactnativenavigation.views.element.animators.TextChangeAnimator;
12
-import com.reactnativenavigation.views.element.animators.XAnimator;
13
-import com.reactnativenavigation.views.element.animators.YAnimator;
14
-
15
-import java.util.ArrayList;
16
-import java.util.Arrays;
17
-import java.util.Collection;
18
-import java.util.Collections;
19
-import java.util.List;
20
-import java.util.Map;
21
-
22
-public class TransitionAnimatorCreator {
23
-
24
-    public Collection<Animator> create(List<Transition> transitions, Map<String, Element> from, Map<String, Element> to) {
25
-        if (transitions.isEmpty()) return Collections.EMPTY_LIST;
26
-        List<Animator> animators = new ArrayList<>();
27
-        for (Transition transition : transitions) {
28
-            animators.addAll(create(transition, from.get(transition.fromId.get()), to.get(transition.toId.get())));
29
-        }
30
-        return animators;
31
-    }
32
-
33
-    protected Collection<? extends Animator> create(Transition transition, Element from, Element to) {
34
-        Collection<Animator> animators = new ArrayList<>();
35
-        for (PropertyAnimatorCreator creator : getAnimators(from, to)) {
36
-            if (creator.shouldAnimateProperty()) animators.add(creator.create(transition));
37
-        }
38
-        return animators;
39
-    }
40
-
41
-    protected List<PropertyAnimatorCreator> getAnimators(Element from, Element to) {
42
-        return Arrays.asList(
43
-                new XAnimator(from, to),
44
-                new YAnimator(from, to),
45
-                new MatrixAnimator(from, to),
46
-                new ScaleXAnimator(from, to),
47
-                new ScaleYAnimator(from, to),
48
-                new BackgroundColorAnimator(from, to),
49
-                new TextChangeAnimator(from, to)
50
-        );
51
-    }
52
-}

+ 137
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/element/TransitionAnimatorCreator.kt View File

@@ -0,0 +1,137 @@
1
+package com.reactnativenavigation.views.element
2
+
3
+import android.animation.Animator
4
+import android.animation.AnimatorListenerAdapter
5
+import android.animation.AnimatorSet
6
+import android.view.View
7
+import android.view.ViewGroup
8
+import android.widget.FrameLayout
9
+import androidx.core.animation.doOnCancel
10
+import androidx.core.animation.doOnEnd
11
+import com.facebook.react.uimanager.ViewGroupManager
12
+import com.reactnativenavigation.R
13
+import com.reactnativenavigation.parse.AnimationOptions
14
+import com.reactnativenavigation.utils.ViewTags
15
+import com.reactnativenavigation.utils.ViewUtils
16
+import java.util.*
17
+
18
+open class TransitionAnimatorCreator {
19
+    fun create(fadeAnimation: AnimationOptions, transitions: TransitionSet): AnimatorSet {
20
+        if (transitions.isEmpty) return AnimatorSet()
21
+        recordIndices(transitions)
22
+        reparentViews(transitions)
23
+        val animators = ArrayList<Animator>()
24
+        animators.addAll(createSharedElementTransitionAnimators(transitions.validSharedElementTransitions))
25
+        animators.addAll(createElementTransitionAnimators(transitions.validElementTransitions))
26
+
27
+        setAnimatorsDuration(animators, fadeAnimation)
28
+        val set = AnimatorSet()
29
+        set.doOnEnd { restoreViewsToOriginalState(transitions) }
30
+        set.doOnCancel { restoreViewsToOriginalState(transitions) }
31
+        set.playTogether(animators)
32
+        return set
33
+    }
34
+
35
+    private fun recordIndices(transitions: TransitionSet) {
36
+        transitions.forEach {
37
+            it.view.setTag(R.id.original_index_in_parent, ViewUtils.getIndexInParent(it.view))
38
+        }
39
+    }
40
+
41
+    private fun setAnimatorsDuration(animators: Collection<Animator>, fadeAnimation: AnimationOptions) {
42
+        for (animator in animators) {
43
+            if (animator is AnimatorSet) {
44
+                setAnimatorsDuration(animator.childAnimations, fadeAnimation)
45
+            } else if (animator.duration.toInt() <= 0) {
46
+                animator.duration = fadeAnimation.duration.toLong()
47
+            }
48
+        }
49
+    }
50
+
51
+    private fun reparentViews(transitions: TransitionSet) {
52
+        transitions.transitions
53
+                .sortedBy { ViewGroupManager.getViewZIndex(it.view) }
54
+                .forEach {
55
+                    reparent(it)
56
+                }
57
+    }
58
+
59
+    private fun createSharedElementTransitionAnimators(transitions: List<SharedElementTransition>): List<AnimatorSet> {
60
+        val animators: MutableList<AnimatorSet> = ArrayList()
61
+        for (transition in transitions) {
62
+            animators.add(createSharedElementAnimator(transition))
63
+        }
64
+        return animators
65
+    }
66
+
67
+    private fun createSharedElementAnimator(transition: SharedElementTransition): AnimatorSet {
68
+        val set = AnimatorSet()
69
+        set.playTogether(transition.createAnimators())
70
+        set.addListener(object : AnimatorListenerAdapter() {
71
+            override fun onAnimationStart(animation: Animator) {
72
+                transition.from.alpha = 0f
73
+            }
74
+        })
75
+        return set
76
+    }
77
+
78
+    private fun createElementTransitionAnimators(transitions: List<ElementTransition>): List<AnimatorSet> {
79
+        val animators: MutableList<AnimatorSet> = ArrayList()
80
+        for (transition in transitions) {
81
+            animators.add(transition.createAnimators())
82
+        }
83
+        return animators
84
+    }
85
+
86
+    private fun restoreViewsToOriginalState(transitions: TransitionSet) {
87
+        mutableListOf<Transition>().apply {
88
+            addAll(transitions.validSharedElementTransitions)
89
+            addAll(transitions.validElementTransitions)
90
+            sortBy { ViewGroupManager.getViewZIndex(it.view) }
91
+            sortBy { it.view.getTag(R.id.original_index_in_parent) as Int}
92
+            forEach {
93
+                it.viewController.requireParentController().removeOverlay(it.view)
94
+                returnToOriginalParent(it.view)
95
+            }
96
+        }
97
+        transitions.validSharedElementTransitions.forEach {
98
+            it.from.alpha = 1f
99
+        }
100
+    }
101
+
102
+    private fun reparent(transition: Transition) {
103
+        with(transition) {
104
+            val biologicalParent = view.parent as ViewGroup
105
+            view.setTag(R.id.original_parent, biologicalParent)
106
+            view.setTag(R.id.original_layout_params, view.layoutParams)
107
+            view.setTag(R.id.original_top, view.top)
108
+            view.setTag(R.id.original_bottom, view.bottom)
109
+            view.setTag(R.id.original_right, view.right)
110
+            view.setTag(R.id.original_left, view.left)
111
+
112
+            val loc = ViewUtils.getLocationOnScreen(view)
113
+            biologicalParent.removeView(view)
114
+
115
+            val lp = FrameLayout.LayoutParams(view.layoutParams)
116
+            lp.topMargin = loc.y + viewController.topInset
117
+            lp.topMargin = loc.y
118
+            lp.leftMargin = loc.x
119
+            lp.width = view.width
120
+            lp.height = view.height
121
+            view.layoutParams = lp
122
+            transition.viewController.requireParentController().addOverlay(view)
123
+        }
124
+    }
125
+
126
+    private fun returnToOriginalParent(element: View) {
127
+        ViewUtils.removeFromParent(element)
128
+        element.top = ViewTags.get(element, R.id.original_top)
129
+        element.bottom = ViewTags.get(element, R.id.original_bottom)
130
+        element.right = ViewTags.get(element, R.id.original_right)
131
+        element.left = ViewTags.get(element, R.id.original_left)
132
+        val parent = ViewTags.get<ViewGroup>(element, R.id.original_parent)
133
+        val lp = ViewTags.get<ViewGroup.LayoutParams>(element, R.id.original_layout_params)
134
+        val index = ViewTags.get<Int>(element, R.id.original_index_in_parent)
135
+        parent.addView(element, index, lp)
136
+    }
137
+}

+ 27
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/element/TransitionSet.kt View File

@@ -0,0 +1,27 @@
1
+package com.reactnativenavigation.views.element
2
+
3
+import java.util.*
4
+
5
+class TransitionSet {
6
+    var validSharedElementTransitions: MutableList<SharedElementTransition> = ArrayList()
7
+    var validElementTransitions: MutableList<ElementTransition> = ArrayList()
8
+    val isEmpty: Boolean
9
+        get() = size() == 0
10
+    val transitions: List<Transition>
11
+        get() = validElementTransitions + validSharedElementTransitions
12
+
13
+    fun add(transition: SharedElementTransition) {
14
+        validSharedElementTransitions.add(transition)
15
+    }
16
+
17
+    fun add(transition: ElementTransition) {
18
+        validElementTransitions.add(transition)
19
+    }
20
+
21
+    fun forEach(action: ((Transition) -> Unit)) {
22
+        validSharedElementTransitions.forEach(action)
23
+        validElementTransitions.forEach(action)
24
+    }
25
+
26
+    fun size(): Int = validElementTransitions.size + validSharedElementTransitions.size
27
+}

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

@@ -1,15 +0,0 @@
1
-package com.reactnativenavigation.views.element;
2
-
3
-import com.reactnativenavigation.parse.Transition;
4
-
5
-import java.util.Map;
6
-
7
-public class TransitionValidator {
8
-    protected boolean validate(Transition transition, Map<String, Element> fromMap, Map<String, Element> toMap) {
9
-        return transition.fromId.hasValue() &&
10
-                fromMap.containsKey(transition.fromId.get()) &&
11
-               transition.toId.hasValue() &&
12
-               toMap.containsKey(transition.toId.get()) &&
13
-               transition.duration.hasValue();
14
-    }
15
-}

+ 0
- 44
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/BackgroundColorAnimator.java View File

@@ -1,44 +0,0 @@
1
-package com.reactnativenavigation.views.element.animators;
2
-
3
-import android.animation.Animator;
4
-import android.animation.ObjectAnimator;
5
-import android.view.ViewGroup;
6
-
7
-import com.facebook.react.views.text.ReactTextView;
8
-import com.facebook.react.views.view.ReactViewBackgroundDrawable;
9
-import com.reactnativenavigation.utils.ColorUtils;
10
-import com.reactnativenavigation.utils.ViewUtils;
11
-import com.reactnativenavigation.views.element.Element;
12
-
13
-import java.util.Collections;
14
-import java.util.List;
15
-
16
-public class BackgroundColorAnimator extends PropertyAnimatorCreator<ViewGroup> {
17
-
18
-    public BackgroundColorAnimator(Element from, Element to) {
19
-        super(from, to);
20
-    }
21
-
22
-    @Override
23
-    public boolean shouldAnimateProperty(ViewGroup fromChild, ViewGroup toChild) {
24
-        return fromChild.getBackground() instanceof ReactViewBackgroundDrawable &&
25
-               toChild.getBackground() instanceof ReactViewBackgroundDrawable &&
26
-               ((ReactViewBackgroundDrawable) fromChild.getBackground()).getColor() != ((ReactViewBackgroundDrawable) toChild.getBackground()).getColor();
27
-    }
28
-
29
-    @Override
30
-    protected List<Class> excludedViews() {
31
-        return Collections.singletonList(ReactTextView.class);
32
-    }
33
-
34
-    @Override
35
-    public Animator create() {
36
-        return ObjectAnimator.ofObject(
37
-                to,
38
-                "backgroundColor",
39
-                new LabColorEvaluator(),
40
-                ColorUtils.colorToLAB(ViewUtils.getBackgroundColor(from.getChild())),
41
-                ColorUtils.colorToLAB(ViewUtils.getBackgroundColor(to.getChild()))
42
-        );
43
-    }
44
-}

+ 28
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/BackgroundColorAnimator.kt View File

@@ -0,0 +1,28 @@
1
+package com.reactnativenavigation.views.element.animators
2
+
3
+import android.animation.Animator
4
+import android.animation.ObjectAnimator
5
+import android.view.View
6
+import android.view.ViewGroup
7
+import com.facebook.react.views.text.ReactTextView
8
+import com.facebook.react.views.view.ReactViewBackgroundDrawable
9
+import com.reactnativenavigation.parse.SharedElementTransitionOptions
10
+import com.reactnativenavigation.utils.ColorUtils
11
+import com.reactnativenavigation.utils.ViewUtils
12
+
13
+class BackgroundColorAnimator(from: View, to: View) : PropertyAnimatorCreator<ViewGroup>(from, to) {
14
+    override fun shouldAnimateProperty(fromChild: ViewGroup, toChild: ViewGroup): Boolean {
15
+        return fromChild.background is ReactViewBackgroundDrawable &&
16
+                toChild.background is ReactViewBackgroundDrawable && (fromChild.background as ReactViewBackgroundDrawable).color != (toChild.background as ReactViewBackgroundDrawable).color
17
+    }
18
+
19
+    override fun excludedViews() = listOf(ReactTextView::class.java)
20
+
21
+    override fun create(options: SharedElementTransitionOptions): Animator {
22
+        return ObjectAnimator.ofObject(
23
+                BackgroundColorEvaluator(to.background as ReactViewBackgroundDrawable),
24
+                ColorUtils.colorToLAB(ViewUtils.getBackgroundColor(from)),
25
+                ColorUtils.colorToLAB(ViewUtils.getBackgroundColor(to))
26
+        ).setDuration(options.getDuration())
27
+    }
28
+}

+ 15
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/BackgroundColorEvaluator.kt View File

@@ -0,0 +1,15 @@
1
+package com.reactnativenavigation.views.element.animators
2
+
3
+import android.animation.TypeEvaluator
4
+import androidx.core.graphics.ColorUtils
5
+import com.facebook.react.views.view.ReactViewBackgroundDrawable
6
+
7
+class BackgroundColorEvaluator(private val background: ReactViewBackgroundDrawable) : TypeEvaluator<DoubleArray> {
8
+    private val color = DoubleArray(3)
9
+
10
+    override fun evaluate(ratio: Float, from: DoubleArray, to: DoubleArray): DoubleArray {
11
+        ColorUtils.blendLAB(from, to, ratio.toDouble(), color)
12
+        background.color = com.reactnativenavigation.utils.ColorUtils.labToColor(color)
13
+        return color
14
+    }
15
+}

+ 0
- 45
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/ClipBoundsEvaluator.java View File

@@ -1,45 +0,0 @@
1
-package com.reactnativenavigation.views.element.animators;
2
-
3
-import android.animation.TypeEvaluator;
4
-import android.graphics.Rect;
5
-
6
-public class ClipBoundsEvaluator implements TypeEvaluator<Rect> {
7
-    private int fromWidth;
8
-    private int fromHeight;
9
-    private int toWidth;
10
-    private int toHeight;
11
-    private final Rect result = new Rect();
12
-
13
-    @Override
14
-    public Rect evaluate(float ratio, Rect from, Rect to) {
15
-        sync(from, to);
16
-
17
-        if (toHeight == fromHeight ) {
18
-            result.bottom = toHeight;
19
-        } else {
20
-            if (toHeight > fromHeight) {
21
-                result.bottom = (int) (toHeight - (toHeight - fromHeight) * (1 - ratio));
22
-            } else {
23
-                result.bottom = (int) (toHeight + (fromHeight - toHeight) * (1 - ratio));
24
-            }
25
-        }
26
-
27
-        if (toWidth == fromWidth) {
28
-            result.right = toWidth;
29
-        } else {
30
-            if (toWidth > fromWidth) {
31
-                result.right = (int) (toWidth - (toWidth - fromWidth) * (1 - ratio));
32
-            } else {
33
-                result.right = (int) (toWidth + (fromWidth - toWidth) * (1 - ratio));
34
-            }
35
-        }
36
-        return result;
37
-    }
38
-
39
-    private void sync(Rect from, Rect to) {
40
-        fromWidth = from.right;
41
-        fromHeight = from.bottom;
42
-        toWidth = to.right;
43
-        toHeight = to.bottom;
44
-    }
45
-}

+ 0
- 14
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/LabColorEvaluator.java View File

@@ -1,14 +0,0 @@
1
-package com.reactnativenavigation.views.element.animators;
2
-
3
-import android.animation.TypeEvaluator;
4
-import androidx.core.graphics.ColorUtils;
5
-
6
-public class LabColorEvaluator implements TypeEvaluator<double[]> {
7
-    private final double[] color = new double[3];
8
-
9
-    @Override
10
-    public double[] evaluate(float ratio, double[] from, double[] to) {
11
-        ColorUtils.blendLAB(from, to, ratio, color);
12
-        return color;
13
-    }
14
-}

+ 0
- 63
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/MatrixAnimator.java View File

@@ -1,63 +0,0 @@
1
-package com.reactnativenavigation.views.element.animators;
2
-
3
-import android.animation.Animator;
4
-import android.animation.AnimatorSet;
5
-import android.animation.ObjectAnimator;
6
-import android.graphics.Rect;
7
-import android.view.View;
8
-
9
-import com.facebook.drawee.drawable.ScalingUtils;
10
-import com.facebook.drawee.generic.GenericDraweeHierarchy;
11
-import com.facebook.drawee.view.DraweeView;
12
-import com.facebook.react.views.image.ReactImageView;
13
-import com.reactnativenavigation.views.element.Element;
14
-
15
-import static com.reactnativenavigation.utils.ViewUtils.areDimensionsEqual;
16
-
17
-public class MatrixAnimator extends PropertyAnimatorCreator<ReactImageView> {
18
-
19
-    public MatrixAnimator(Element from, Element to) {
20
-        super(from, to);
21
-    }
22
-
23
-    @Override
24
-    public boolean shouldAnimateProperty(ReactImageView fromChild, ReactImageView toChild) {
25
-        return !areDimensionsEqual(from.getChild(), to.getChild());
26
-    }
27
-
28
-    @Override
29
-    public Animator create() {
30
-        AnimatorSet set = new AnimatorSet();
31
-        set.playTogether(clipBoundsAnimator(), imageTransformAnimator());
32
-        return set;
33
-    }
34
-
35
-    private Animator clipBoundsAnimator() {
36
-        Rect startDrawingRect = new Rect(); from.getDrawingRect(startDrawingRect);
37
-        Rect endDrawingRect = new Rect(); to.getDrawingRect(endDrawingRect);
38
-        return ObjectAnimator.ofObject(to,
39
-                "clipBounds",
40
-                new ClipBoundsEvaluator(),
41
-                startDrawingRect,
42
-                endDrawingRect);
43
-    }
44
-
45
-    private Animator imageTransformAnimator() {
46
-        ScalingUtils.InterpolatingScaleType ist = new ScalingUtils.InterpolatingScaleType(
47
-                getScaleType(from.getChild()),
48
-                getScaleType(to.getChild()),
49
-                calculateBounds(from.getChild()),
50
-                calculateBounds(to.getChild())
51
-        );
52
-        ((DraweeView<GenericDraweeHierarchy>) to.getChild()).getHierarchy().setActualImageScaleType(ist);
53
-        return ObjectAnimator.ofFloat(to, "matrixTransform", 0, 1);
54
-    }
55
-
56
-    private ScalingUtils.ScaleType getScaleType(View child) {
57
-        return ((DraweeView<GenericDraweeHierarchy>) child).getHierarchy().getActualImageScaleType();
58
-    }
59
-
60
-    private Rect calculateBounds(View view) {
61
-        return new Rect(0, 0, view.getWidth(), view.getHeight());
62
-    }
63
-}

+ 39
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/MatrixAnimator.kt View File

@@ -0,0 +1,39 @@
1
+package com.reactnativenavigation.views.element.animators
2
+
3
+import android.animation.Animator
4
+import android.animation.ObjectAnimator
5
+import android.animation.TypeEvaluator
6
+import android.graphics.Rect
7
+import android.view.View
8
+import com.facebook.drawee.drawable.ScalingUtils.InterpolatingScaleType
9
+import com.facebook.react.views.image.ReactImageView
10
+import com.reactnativenavigation.parse.SharedElementTransitionOptions
11
+import com.reactnativenavigation.utils.ViewUtils
12
+
13
+class MatrixAnimator(from: View, to: View) : PropertyAnimatorCreator<ReactImageView>(from, to) {
14
+    override fun shouldAnimateProperty(fromChild: ReactImageView, toChild: ReactImageView): Boolean {
15
+        return !ViewUtils.areDimensionsEqual(from, to)
16
+    }
17
+
18
+    override fun create(options: SharedElementTransitionOptions): Animator {
19
+        with(to as ReactImageView) {
20
+            hierarchy.actualImageScaleType = InterpolatingScaleType(
21
+                    getScaleType(from),
22
+                    getScaleType(to),
23
+                    calculateBounds(from),
24
+                    calculateBounds(to)
25
+            )
26
+            return ObjectAnimator.ofObject(TypeEvaluator<Float> { fraction: Float, _: Any, _: Any ->
27
+                hierarchy.actualImageScaleType?.let {
28
+                    (hierarchy.actualImageScaleType as InterpolatingScaleType?)!!.value = fraction
29
+                    to.invalidate()
30
+                }
31
+                null
32
+            }, 0, 1).setDuration(options.getDuration())
33
+        }
34
+    }
35
+
36
+    private fun getScaleType(child: View) = (child as ReactImageView).hierarchy.actualImageScaleType
37
+
38
+    private fun calculateBounds(view: View): Rect = Rect(0, 0, view.width, view.height)
39
+}

+ 0
- 63
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/PropertyAnimatorCreator.java View File

@@ -1,63 +0,0 @@
1
-package com.reactnativenavigation.views.element.animators;
2
-
3
-import android.animation.Animator;
4
-import android.animation.AnimatorListenerAdapter;
5
-import androidx.annotation.CallSuper;
6
-import android.view.ViewGroup;
7
-
8
-import com.reactnativenavigation.parse.Transition;
9
-import com.reactnativenavigation.views.element.Element;
10
-
11
-import java.lang.reflect.ParameterizedType;
12
-import java.util.Collections;
13
-import java.util.List;
14
-
15
-public abstract class PropertyAnimatorCreator<T> {
16
-
17
-    protected Element from;
18
-    protected Element to;
19
-
20
-    PropertyAnimatorCreator(Element from, Element to) {
21
-        this.from = from;
22
-        this.to = to;
23
-    }
24
-
25
-    @CallSuper
26
-    public boolean shouldAnimateProperty() {
27
-        Class<T> type = getChildClass();
28
-        return type.isInstance(from.getChild()) &&
29
-               type.isInstance(to.getChild()) &&
30
-               !excludedViews().contains(from.getChild().getClass()) &&
31
-               !excludedViews().contains(to.getChild().getClass()) &&
32
-               shouldAnimateProperty((T) from.getChild(), (T) to.getChild());
33
-    }
34
-
35
-    protected abstract boolean shouldAnimateProperty(T fromChild, T toChild);
36
-
37
-    protected List<Class> excludedViews() {
38
-        return Collections.EMPTY_LIST;
39
-    }
40
-
41
-    public Animator create(Transition transition) {
42
-        Animator animator = create().setDuration(transition.duration.get());
43
-        animator.addListener(new AnimatorListenerAdapter() {
44
-            private final boolean originalClipChildren = ((ViewGroup) to.getParent()).getClipChildren();
45
-            @Override
46
-            public void onAnimationStart(Animator animation) {
47
-                ((ViewGroup) to.getParent()).setClipChildren(false);
48
-            }
49
-
50
-            @Override
51
-            public void onAnimationEnd(Animator animation) {
52
-                ((ViewGroup) to.getParent()).setClipChildren(originalClipChildren);
53
-            }
54
-        });
55
-        return animator;
56
-    }
57
-
58
-    protected abstract Animator create();
59
-
60
-    private Class<T> getChildClass() {
61
-        return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
62
-    }
63
-}

+ 27
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/PropertyAnimatorCreator.kt View File

@@ -0,0 +1,27 @@
1
+package com.reactnativenavigation.views.element.animators
2
+
3
+import android.animation.Animator
4
+import android.view.View
5
+import androidx.annotation.CallSuper
6
+import com.reactnativenavigation.parse.SharedElementTransitionOptions
7
+import java.lang.reflect.ParameterizedType
8
+
9
+abstract class PropertyAnimatorCreator<T : View> internal constructor(protected var from: View, protected var to: View) {
10
+    @CallSuper
11
+    fun shouldAnimateProperty(): Boolean {
12
+        val type = childClass
13
+        return type.isInstance(from) &&
14
+                type.isInstance(to) &&
15
+                !excludedViews().contains(from.javaClass) &&
16
+                !excludedViews().contains(to.javaClass) &&
17
+                shouldAnimateProperty(from as T, to as T)
18
+    }
19
+
20
+    protected abstract fun shouldAnimateProperty(fromChild: T, toChild: T): Boolean
21
+    protected open fun excludedViews() = emptyList<Class<*>>()
22
+
23
+    abstract fun create(options: SharedElementTransitionOptions): Animator
24
+    private val childClass: Class<T>
25
+        get() = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
26
+
27
+}

+ 0
- 39
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/ScaleXAnimator.java View File

@@ -1,39 +0,0 @@
1
-package com.reactnativenavigation.views.element.animators;
2
-
3
-import android.animation.Animator;
4
-import android.animation.ObjectAnimator;
5
-import android.view.View;
6
-import android.view.ViewGroup;
7
-
8
-import com.facebook.react.views.text.ReactTextView;
9
-import com.reactnativenavigation.views.element.Element;
10
-
11
-import java.util.Collections;
12
-import java.util.List;
13
-
14
-public class ScaleXAnimator extends PropertyAnimatorCreator<ViewGroup> {
15
-
16
-    public ScaleXAnimator(Element from, Element to) {
17
-        super(from, to);
18
-    }
19
-
20
-    @Override
21
-    public boolean shouldAnimateProperty(ViewGroup fromChild, ViewGroup toChild) {
22
-        return fromChild.getChildCount() == 0 && toChild.getChildCount() == 0;
23
-    }
24
-
25
-    @Override
26
-    protected List<Class> excludedViews() {
27
-        return Collections.singletonList(ReactTextView.class);
28
-    }
29
-
30
-    @Override
31
-    public Animator create() {
32
-        return ObjectAnimator.ofFloat(
33
-                to.getChild(),
34
-                View.SCALE_X,
35
-                ((float) from.getChild().getWidth()) / to.getChild().getWidth(),
36
-                1
37
-        );
38
-    }
39
-}

+ 22
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/ScaleXAnimator.kt View File

@@ -0,0 +1,22 @@
1
+package com.reactnativenavigation.views.element.animators
2
+
3
+import android.animation.Animator
4
+import android.animation.ObjectAnimator
5
+import android.view.View
6
+import android.view.ViewGroup
7
+import com.facebook.react.views.text.ReactTextView
8
+import com.reactnativenavigation.parse.SharedElementTransitionOptions
9
+
10
+class ScaleXAnimator(from: View, to: View) : PropertyAnimatorCreator<ViewGroup>(from, to) {
11
+    override fun shouldAnimateProperty(fromChild: ViewGroup, toChild: ViewGroup): Boolean {
12
+        return fromChild.childCount == 0 && toChild.childCount == 0
13
+    }
14
+
15
+    override fun excludedViews(): List<Class<*>> = listOf<Class<*>>(ReactTextView::class.java)
16
+
17
+    override fun create(options: SharedElementTransitionOptions): Animator {
18
+        return ObjectAnimator
19
+                .ofFloat(to, View.SCALE_X, from.width.toFloat() / to.width, 1f)
20
+                .setDuration(options.getDuration())
21
+    }
22
+}

+ 0
- 39
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/ScaleYAnimator.java View File

@@ -1,39 +0,0 @@
1
-package com.reactnativenavigation.views.element.animators;
2
-
3
-import android.animation.Animator;
4
-import android.animation.ObjectAnimator;
5
-import android.view.View;
6
-import android.view.ViewGroup;
7
-
8
-import com.facebook.react.views.text.ReactTextView;
9
-import com.reactnativenavigation.views.element.Element;
10
-
11
-import java.util.Collections;
12
-import java.util.List;
13
-
14
-public class ScaleYAnimator extends PropertyAnimatorCreator<ViewGroup> {
15
-
16
-    public ScaleYAnimator(Element from, Element to) {
17
-        super(from, to);
18
-    }
19
-
20
-    @Override
21
-    public boolean shouldAnimateProperty(ViewGroup fromChild, ViewGroup toChild) {
22
-        return fromChild.getChildCount() == 0 && toChild.getChildCount() == 0;
23
-    }
24
-
25
-    @Override
26
-    protected List<Class> excludedViews() {
27
-        return Collections.singletonList(ReactTextView.class);
28
-    }
29
-
30
-    @Override
31
-    public Animator create() {
32
-        return ObjectAnimator.ofFloat(
33
-                to.getChild(),
34
-                View.SCALE_Y,
35
-                ((float) from.getChild().getHeight()) / to.getChild().getHeight(),
36
-                1
37
-        );
38
-    }
39
-}

+ 22
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/ScaleYAnimator.kt View File

@@ -0,0 +1,22 @@
1
+package com.reactnativenavigation.views.element.animators
2
+
3
+import android.animation.Animator
4
+import android.animation.ObjectAnimator
5
+import android.view.View
6
+import android.view.ViewGroup
7
+import com.facebook.react.views.text.ReactTextView
8
+import com.reactnativenavigation.parse.SharedElementTransitionOptions
9
+
10
+class ScaleYAnimator(from: View, to: View) : PropertyAnimatorCreator<ViewGroup>(from, to) {
11
+    override fun shouldAnimateProperty(fromChild: ViewGroup, toChild: ViewGroup): Boolean {
12
+        return fromChild.childCount == 0 && toChild.childCount == 0
13
+    }
14
+
15
+    override fun excludedViews() = listOf(ReactTextView::class.java)
16
+
17
+    override fun create(options: SharedElementTransitionOptions): Animator {
18
+        return ObjectAnimator
19
+                .ofFloat(to, View.SCALE_Y, from.height.toFloat() / to.height, 1f)
20
+                .setDuration(options.getDuration())
21
+    }
22
+}

+ 0
- 35
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/TextChangeAnimator.java View File

@@ -1,35 +0,0 @@
1
-package com.reactnativenavigation.views.element.animators;
2
-
3
-import android.animation.Animator;
4
-import android.graphics.Point;
5
-import android.widget.TextView;
6
-
7
-import com.facebook.react.views.text.ReactTextView;
8
-import com.reactnativenavigation.utils.ViewUtils;
9
-import com.reactnativenavigation.views.element.Element;
10
-import com.shazam.android.widget.text.reflow.ReflowTextAnimatorHelper;
11
-
12
-import static com.reactnativenavigation.utils.TextViewUtils.getTextSize;
13
-
14
-public class TextChangeAnimator extends PropertyAnimatorCreator<ReactTextView> {
15
-
16
-    public TextChangeAnimator(Element from, Element to) {
17
-        super(from, to);
18
-    }
19
-
20
-    @Override
21
-    protected boolean shouldAnimateProperty(ReactTextView fromChild, ReactTextView toChild) {
22
-        Point fromXy = ViewUtils.getLocationOnScreen(from.getChild());
23
-        Point toXy = ViewUtils.getLocationOnScreen(to.getChild());
24
-        return getTextSize(fromChild) != getTextSize(toChild) ||
25
-               !fromXy.equals(toXy.x, toXy.y);
26
-    }
27
-
28
-    @Override
29
-    public Animator create() {
30
-        return new ReflowTextAnimatorHelper
31
-                .Builder((TextView) from.getChild(), (TextView) to.getChild())
32
-                .calculateDuration(false)
33
-                .buildAnimator();
34
-    }
35
-}

+ 43
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/TextChangeAnimator.kt View File

@@ -0,0 +1,43 @@
1
+package com.reactnativenavigation.views.element.animators
2
+
3
+import android.animation.Animator
4
+import android.graphics.Rect
5
+import android.view.View
6
+import android.view.ViewGroup
7
+import android.widget.TextView
8
+import com.facebook.react.views.text.ReactTextView
9
+import com.reactnativenavigation.parse.SharedElementTransitionOptions
10
+import com.reactnativenavigation.utils.TextViewUtils
11
+import com.reactnativenavigation.utils.ViewUtils
12
+import com.shazam.android.widget.text.reflow.ReflowTextAnimatorHelper
13
+
14
+class TextChangeAnimator(from: View, to: View) : PropertyAnimatorCreator<ReactTextView>(from, to) {
15
+    override fun shouldAnimateProperty(fromChild: ReactTextView, toChild: ReactTextView): Boolean {
16
+        val fromXy = ViewUtils.getLocationOnScreen(from)
17
+        val toXy = ViewUtils.getLocationOnScreen(to)
18
+        return TextViewUtils.getTextSize(fromChild) != TextViewUtils.getTextSize(toChild) ||
19
+                !fromXy.equals(toXy.x, toXy.y)
20
+    }
21
+
22
+    override fun create(options: SharedElementTransitionOptions): Animator {
23
+        return ReflowTextAnimatorHelper.Builder(from as TextView, to as TextView)
24
+                .calculateDuration(false)
25
+                .setBoundsCalculator { view: View ->
26
+                    val loc = IntArray(2)
27
+                    view.getLocationInWindow(loc)
28
+                    val x = loc[0]
29
+                    val y = if (view == to) (to.layoutParams as ViewGroup.MarginLayoutParams).topMargin else loc[1]
30
+                    Rect(
31
+                            x,
32
+                            y,
33
+                            x + view.width,
34
+                            y + view.height
35
+                    )
36
+                }
37
+                .setTextColorGetter {
38
+                    TextViewUtils.getTextColor(it)
39
+                }
40
+                .buildAnimator()
41
+                .setDuration(options.getDuration())
42
+    }
43
+}

+ 0
- 35
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/TextColorAnimator.java View File

@@ -1,35 +0,0 @@
1
-package com.reactnativenavigation.views.element.animators;
2
-
3
-import android.animation.Animator;
4
-import android.animation.ObjectAnimator;
5
-import android.widget.TextView;
6
-
7
-import com.facebook.react.views.text.ReactTextView;
8
-import com.reactnativenavigation.utils.ColorUtils;
9
-import com.reactnativenavigation.utils.TextViewUtils;
10
-import com.reactnativenavigation.views.element.Element;
11
-
12
-import static com.reactnativenavigation.utils.TextViewUtils.getTextColor;
13
-
14
-public class TextColorAnimator extends PropertyAnimatorCreator<ReactTextView> {
15
-
16
-    public TextColorAnimator(Element from, Element to) {
17
-        super(from, to);
18
-    }
19
-
20
-    @Override
21
-    protected boolean shouldAnimateProperty(ReactTextView fromChild, ReactTextView toChild) {
22
-        return getTextColor(fromChild) != getTextColor(toChild);
23
-    }
24
-
25
-    @Override
26
-    public Animator create() {
27
-        return ObjectAnimator.ofObject(
28
-                to,
29
-                "textColor",
30
-                new LabColorEvaluator(),
31
-                ColorUtils.colorToLAB(TextViewUtils.getTextColor((TextView) from.getChild())),
32
-                ColorUtils.colorToLAB(TextViewUtils.getTextColor((TextView) to.getChild()))
33
-        );
34
-    }
35
-}

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

@@ -1,33 +0,0 @@
1
-package com.reactnativenavigation.views.element.animators;
2
-
3
-import android.animation.Animator;
4
-import android.animation.ObjectAnimator;
5
-import android.widget.TextView;
6
-
7
-import com.facebook.react.views.text.ReactTextView;
8
-import com.reactnativenavigation.utils.TextViewUtils;
9
-import com.reactnativenavigation.views.element.Element;
10
-
11
-import static com.reactnativenavigation.utils.TextViewUtils.getTextSize;
12
-
13
-public class TextSizeAnimator extends PropertyAnimatorCreator<ReactTextView> {
14
-
15
-    public TextSizeAnimator(Element from, Element to) {
16
-        super(from, to);
17
-    }
18
-
19
-    @Override
20
-    protected boolean shouldAnimateProperty(ReactTextView fromChild, ReactTextView toChild) {
21
-        return getTextSize(fromChild) != getTextSize(toChild);
22
-    }
23
-
24
-    @Override
25
-    public Animator create() {
26
-        return ObjectAnimator.ofFloat(
27
-                to,
28
-                "textSize",
29
-                TextViewUtils.getTextSize((TextView) from.getChild()),
30
-                TextViewUtils.getTextSize((TextView) to.getChild())
31
-        );
32
-    }
33
-}

+ 0
- 40
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/XAnimator.java View File

@@ -1,40 +0,0 @@
1
-package com.reactnativenavigation.views.element.animators;
2
-
3
-import android.animation.Animator;
4
-import android.animation.ObjectAnimator;
5
-import android.graphics.Point;
6
-import android.view.View;
7
-
8
-import com.facebook.react.views.text.ReactTextView;
9
-import com.reactnativenavigation.utils.ViewUtils;
10
-import com.reactnativenavigation.views.element.Element;
11
-
12
-import java.util.Collections;
13
-import java.util.List;
14
-
15
-public class XAnimator extends PropertyAnimatorCreator<View> {
16
-
17
-    private final int dx;
18
-
19
-    public XAnimator(Element from, Element to) {
20
-        super(from, to);
21
-        final Point fromXy = ViewUtils.getLocationOnScreen(from.getChild());
22
-        final Point toXy = ViewUtils.getLocationOnScreen(to.getChild());
23
-        dx = fromXy.x - toXy.x;
24
-    }
25
-
26
-    @Override
27
-    protected List<Class> excludedViews() {
28
-        return Collections.singletonList(ReactTextView.class);
29
-    }
30
-
31
-    @Override
32
-    public boolean shouldAnimateProperty(View fromChild, View toChild) {
33
-        return dx != 0;
34
-    }
35
-
36
-    @Override
37
-    public Animator create() {
38
-        return ObjectAnimator.ofFloat(to.getChild(), View.TRANSLATION_X, dx, 0);
39
-    }
40
-}

+ 28
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/XAnimator.kt View File

@@ -0,0 +1,28 @@
1
+package com.reactnativenavigation.views.element.animators
2
+
3
+import android.animation.Animator
4
+import android.animation.ObjectAnimator
5
+import android.view.View
6
+import android.view.View.TRANSLATION_X
7
+import com.facebook.react.views.text.ReactTextView
8
+import com.reactnativenavigation.parse.SharedElementTransitionOptions
9
+import com.reactnativenavigation.utils.ViewUtils
10
+
11
+class XAnimator(from: View, to: View) : PropertyAnimatorCreator<View>(from, to) {
12
+    private val dx: Int
13
+
14
+    init {
15
+        val fromXy = ViewUtils.getLocationOnScreen(from)
16
+        val toXy = ViewUtils.getLocationOnScreen(to)
17
+        dx = fromXy.x - toXy.x
18
+        to.pivotX = 0f
19
+    }
20
+
21
+    override fun excludedViews() = listOf(ReactTextView::class.java)
22
+
23
+    override fun shouldAnimateProperty(fromChild: View, toChild: View) = dx != 0
24
+
25
+    override fun create(options: SharedElementTransitionOptions): Animator {
26
+        return ObjectAnimator.ofFloat(to, TRANSLATION_X, dx.toFloat(), 0f).setDuration(options.getDuration())
27
+    }
28
+}

+ 0
- 40
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/YAnimator.java View File

@@ -1,40 +0,0 @@
1
-package com.reactnativenavigation.views.element.animators;
2
-
3
-import android.animation.Animator;
4
-import android.animation.ObjectAnimator;
5
-import android.graphics.Point;
6
-import android.view.View;
7
-
8
-import com.facebook.react.views.text.ReactTextView;
9
-import com.reactnativenavigation.utils.ViewUtils;
10
-import com.reactnativenavigation.views.element.Element;
11
-
12
-import java.util.Collections;
13
-import java.util.List;
14
-
15
-public class YAnimator extends PropertyAnimatorCreator<View> {
16
-
17
-    private final int dy;
18
-
19
-    public YAnimator(Element from, Element to) {
20
-        super(from, to);
21
-        final Point fromXy = ViewUtils.getLocationOnScreen(from.getChild());
22
-        final Point toXy = ViewUtils.getLocationOnScreen(to.getChild());
23
-        dy = fromXy.y - toXy.y;
24
-    }
25
-
26
-    @Override
27
-    protected List<Class> excludedViews() {
28
-        return Collections.singletonList(ReactTextView.class);
29
-    }
30
-
31
-    @Override
32
-    public boolean shouldAnimateProperty(View fromChild, View toChild) {
33
-        return dy != 0;
34
-    }
35
-
36
-    @Override
37
-    public Animator create() {
38
-        return ObjectAnimator.ofFloat(to.getChild(), View.TRANSLATION_Y, dy, 0);
39
-    }
40
-}

+ 29
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/YAnimator.kt View File

@@ -0,0 +1,29 @@
1
+package com.reactnativenavigation.views.element.animators
2
+
3
+import android.animation.Animator
4
+import android.animation.ObjectAnimator
5
+import android.view.View
6
+import android.view.View.TRANSLATION_Y
7
+import android.view.ViewGroup
8
+import com.facebook.react.views.text.ReactTextView
9
+import com.reactnativenavigation.parse.SharedElementTransitionOptions
10
+import com.reactnativenavigation.utils.ViewUtils
11
+
12
+class YAnimator(from: View, to: View) : PropertyAnimatorCreator<View>(from, to) {
13
+    private val dy: Int
14
+
15
+    init {
16
+        val fromXy = ViewUtils.getLocationOnScreen(from)
17
+        val toY = (to.layoutParams as ViewGroup.MarginLayoutParams).topMargin
18
+        dy = fromXy.y - toY
19
+        to.pivotY = 0f
20
+    }
21
+
22
+    override fun shouldAnimateProperty(fromChild: View, toChild: View) = dy != 0
23
+
24
+    override fun excludedViews() = listOf(ReactTextView::class.java)
25
+
26
+    override fun create(options: SharedElementTransitionOptions): Animator {
27
+        return ObjectAnimator.ofFloat(to, TRANSLATION_Y, dy.toFloat(), 0f).setDuration(options.getDuration())
28
+    }
29
+}

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

@@ -28,6 +28,7 @@ import androidx.appcompat.widget.Toolbar;
28 28
 import static com.reactnativenavigation.utils.ObjectUtils.perform;
29 29
 import static com.reactnativenavigation.utils.UiUtils.runOnPreDrawOnce;
30 30
 import static com.reactnativenavigation.utils.ViewUtils.findChildByClass;
31
+import static com.reactnativenavigation.utils.ViewUtils.findChildrenByClass;
31 32
 
32 33
 @SuppressLint("ViewConstructor")
33 34
 public class TitleBar extends Toolbar {
@@ -109,11 +110,10 @@ public class TitleBar extends Toolbar {
109 110
 
110 111
     public void alignTextView(Alignment alignment, TextView view) {
111 112
         if (StringUtils.isEmpty(view.getText())) return;
112
-        Integer direction = view.getParent().getLayoutDirection();
113
+        int direction = view.getParent().getLayoutDirection();
113 114
         boolean isRTL = direction == View.LAYOUT_DIRECTION_RTL;
114 115
 
115 116
         if (alignment == Alignment.Center) {
116
-            //noinspection IntegerDivisionInFloatingPointContext
117 117
             view.setX((getWidth() - view.getWidth()) / 2);
118 118
         } else if (leftButtonController != null) {
119 119
             view.setX(isRTL ? (getWidth() - view.getWidth()) - getContentInsetStartWithNavigation() : getContentInsetStartWithNavigation());
@@ -128,17 +128,13 @@ public class TitleBar extends Toolbar {
128 128
 
129 129
         if(changed || isTitleChanged) {
130 130
             TextView title = findTitleTextView();
131
-            if (title != null) {
132
-                this.alignTextView(titleAlignment, title);
133
-            }
131
+            if (title != null) this.alignTextView(titleAlignment, title);
134 132
             isTitleChanged = false;
135 133
         }
136 134
 
137 135
         if(changed || isSubtitleChanged) {
138 136
             TextView subtitle = findSubtitleTextView();
139
-            if (subtitle != null) {
140
-                this.alignTextView(subtitleAlignment, subtitle);
141
-            }
137
+            if (subtitle != null) this.alignTextView(subtitleAlignment, subtitle);
142 138
             isSubtitleChanged = false;
143 139
         }
144 140
     }
@@ -151,13 +147,13 @@ public class TitleBar extends Toolbar {
151 147
 
152 148
     @Nullable
153 149
     public TextView findTitleTextView() {
154
-        List<TextView> children = ViewUtils.findChildrenByClass(this, TextView.class, textView -> textView.getText().equals(getTitle()));
150
+        List<TextView> children = findChildrenByClass(this, TextView.class, textView -> textView.getText().equals(getTitle()));
155 151
         return children.isEmpty() ? null : children.get(0);
156 152
     }
157 153
 
158 154
     @Nullable
159 155
     public TextView findSubtitleTextView() {
160
-        List<TextView> children = ViewUtils.findChildrenByClass(this, TextView.class, textView -> textView.getText().equals(getSubtitle()));
156
+        List<TextView> children = findChildrenByClass(this, TextView.class, textView -> textView.getText().equals(getSubtitle()));
161 157
         return children.isEmpty() ? null : children.get(0);
162 158
     }
163 159
 

+ 8
- 0
lib/android/app/src/main/res/values/ids.xml View File

@@ -5,4 +5,12 @@
5 5
     <item name="topBarBackgroundComponent" type="id"/>
6 6
     <item name="bottomTabs" type="id"/>
7 7
     <item name="fab_bottom_margin" type="id" />
8
+
9
+    <item name="original_parent" type="id" />
10
+    <item name="original_layout_params" type="id" />
11
+    <item name="original_index_in_parent" type="id" />
12
+    <item name="original_top" type="id" />
13
+    <item name="original_bottom" type="id" />
14
+    <item name="original_right" type="id" />
15
+    <item name="original_left" type="id" />
8 16
 </resources>

+ 4
- 42
lib/android/app/src/reactNative51/java/com/reactnativenavigation/react/NavigationReactNativeHost.java View File

@@ -1,9 +1,5 @@
1 1
 package com.reactnativenavigation.react;
2 2
 
3
-import android.app.Application;
4
-import androidx.annotation.NonNull;
5
-import androidx.annotation.Nullable;
6
-
7 3
 import com.facebook.infer.annotation.Assertions;
8 4
 import com.facebook.react.ReactInstanceManager;
9 5
 import com.facebook.react.ReactInstanceManagerBuilder;
@@ -11,20 +7,17 @@ import com.facebook.react.ReactNativeHost;
11 7
 import com.facebook.react.ReactPackage;
12 8
 import com.facebook.react.common.LifecycleState;
13 9
 import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
14
-import com.facebook.react.shell.MainReactPackage;
15 10
 import com.reactnativenavigation.NavigationApplication;
16 11
 
17
-import java.util.ArrayList;
18
-import java.util.List;
12
+import androidx.annotation.NonNull;
13
+import androidx.annotation.Nullable;
19 14
 
20 15
 /**
21 16
  * Default implementation of {@link ReactNativeHost} that includes {@link NavigationPackage}
22 17
  * and user-defined additional packages.
23 18
  */
24
-public class NavigationReactNativeHost extends ReactNativeHost implements BundleDownloadListenerProvider {
19
+public abstract class NavigationReactNativeHost extends ReactNativeHost implements BundleDownloadListenerProvider {
25 20
 
26
-    private final boolean isDebug;
27
-    private final List<ReactPackage> additionalReactPackages;
28 21
     private @Nullable NavigationDevBundleDownloadListener bundleListener;
29 22
     private final DevBundleDownloadListener bundleListenerMediator = new DevBundleDownloadListenerAdapter() {
30 23
         @Override
@@ -35,16 +28,9 @@ public class NavigationReactNativeHost extends ReactNativeHost implements Bundle
35 28
         }
36 29
     };
37 30
 
38
-
39
-    public NavigationReactNativeHost(NavigationApplication application) {
40
-        this(application, application.isDebug(), application.createAdditionalReactPackages());
41
-    }
42
-
43 31
     @SuppressWarnings("WeakerAccess")
44
-    public NavigationReactNativeHost(Application application, boolean isDebug, final List<ReactPackage> additionalReactPackages) {
32
+    public NavigationReactNativeHost(NavigationApplication application) {
45 33
         super(application);
46
-        this.isDebug = isDebug;
47
-        this.additionalReactPackages = additionalReactPackages;
48 34
     }
49 35
 
50 36
     @Override
@@ -52,30 +38,6 @@ public class NavigationReactNativeHost extends ReactNativeHost implements Bundle
52 38
         bundleListener = listener;
53 39
     }
54 40
 
55
-    @Override
56
-    public boolean getUseDeveloperSupport() {
57
-        return isDebug;
58
-    }
59
-
60
-    @Override
61
-    protected List<ReactPackage> getPackages() {
62
-        List<ReactPackage> packages = new ArrayList<>();
63
-        boolean hasMainReactPackage = false;
64
-        packages.add(new NavigationPackage(this));
65
-        if (additionalReactPackages != null) {
66
-            for (ReactPackage p : additionalReactPackages) {
67
-                if (!(p instanceof NavigationPackage)) {
68
-                    packages.add(p);
69
-                }
70
-                if (p instanceof MainReactPackage) hasMainReactPackage = true;
71
-            }
72
-        }
73
-        if (!hasMainReactPackage) {
74
-            packages.add(new MainReactPackage());
75
-        }
76
-        return packages;
77
-    }
78
-
79 41
     protected ReactInstanceManager createReactInstanceManager() {
80 42
         ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
81 43
                 .setApplication(getApplication())

+ 5
- 42
lib/android/app/src/reactNative55/java/com/reactnativenavigation/react/NavigationReactNativeHost.java View File

@@ -1,9 +1,5 @@
1 1
 package com.reactnativenavigation.react;
2 2
 
3
-import android.app.Application;
4
-import androidx.annotation.NonNull;
5
-import androidx.annotation.Nullable;
6
-
7 3
 import com.facebook.infer.annotation.Assertions;
8 4
 import com.facebook.react.ReactInstanceManager;
9 5
 import com.facebook.react.ReactInstanceManagerBuilder;
@@ -11,20 +7,17 @@ import com.facebook.react.ReactNativeHost;
11 7
 import com.facebook.react.ReactPackage;
12 8
 import com.facebook.react.common.LifecycleState;
13 9
 import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
14
-import com.facebook.react.shell.MainReactPackage;
15 10
 import com.reactnativenavigation.NavigationApplication;
16 11
 
17
-import java.util.ArrayList;
18
-import java.util.List;
12
+import androidx.annotation.NonNull;
13
+import androidx.annotation.Nullable;
19 14
 
20 15
 /**
21 16
  * Default implementation of {@link ReactNativeHost} that includes {@link NavigationPackage}
22 17
  * and user-defined additional packages.
23 18
  */
24
-public class NavigationReactNativeHost extends ReactNativeHost implements BundleDownloadListenerProvider {
19
+public abstract class NavigationReactNativeHost extends ReactNativeHost implements BundleDownloadListenerProvider {
25 20
 
26
-    private final boolean isDebug;
27
-    private final List<ReactPackage> additionalReactPackages;
28 21
     private @Nullable NavigationDevBundleDownloadListener bundleListener;
29 22
     private final DevBundleDownloadListener bundleListenerMediator = new DevBundleDownloadListenerAdapter() {
30 23
         @Override
@@ -35,16 +28,9 @@ public class NavigationReactNativeHost extends ReactNativeHost implements Bundle
35 28
         }
36 29
     };
37 30
 
38
-
39
-    public NavigationReactNativeHost(NavigationApplication application) {
40
-        this(application, application.isDebug(), application.createAdditionalReactPackages());
41
-    }
42
-
43 31
     @SuppressWarnings("WeakerAccess")
44
-    public NavigationReactNativeHost(Application application, boolean isDebug, final List<ReactPackage> additionalReactPackages) {
32
+    public NavigationReactNativeHost(NavigationApplication application) {
45 33
         super(application);
46
-        this.isDebug = isDebug;
47
-        this.additionalReactPackages = additionalReactPackages;
48 34
     }
49 35
 
50 36
     @Override
@@ -52,30 +38,6 @@ public class NavigationReactNativeHost extends ReactNativeHost implements Bundle
52 38
         bundleListener = listener;
53 39
     }
54 40
 
55
-    @Override
56
-    public boolean getUseDeveloperSupport() {
57
-        return isDebug;
58
-    }
59
-
60
-    @Override
61
-    protected List<ReactPackage> getPackages() {
62
-        List<ReactPackage> packages = new ArrayList<>();
63
-        boolean hasMainReactPackage = false;
64
-        packages.add(new NavigationPackage(this));
65
-        if (additionalReactPackages != null) {
66
-            for (ReactPackage p : additionalReactPackages) {
67
-                if (!(p instanceof NavigationPackage)) {
68
-                    packages.add(p);
69
-                }
70
-                if (p instanceof MainReactPackage) hasMainReactPackage = true;
71
-            }
72
-        }
73
-        if (!hasMainReactPackage) {
74
-            packages.add(new MainReactPackage());
75
-        }
76
-        return packages;
77
-    }
78
-
79 41
     protected ReactInstanceManager createReactInstanceManager() {
80 42
         ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
81 43
                 .setApplication(getApplication())
@@ -85,6 +47,7 @@ public class NavigationReactNativeHost extends ReactNativeHost implements Bundle
85 47
                 .setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
86 48
                 .setUIImplementationProvider(getUIImplementationProvider())
87 49
                 .setInitialLifecycleState(LifecycleState.BEFORE_CREATE)
50
+                .setJSIModulesProvider(getJSIModulesProvider())
88 51
                 .setDevBundleDownloadListener(getDevBundleDownloadListener());
89 52
 
90 53
         for (ReactPackage reactPackage : getPackages()) {

+ 0
- 0
lib/android/app/src/reactNative56/java/com/reactnativenavigation/react/NavigationReactNativeHost.java View File


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